SRM 671, D2, 250-Pointer (BearCries)

James S. Plank

Fri Oct 16 16:11:29 EDT 2015

In Case Topcoder's Servers are Down

Here is a summary of the problem:

Examples

0: ";_;;_____;" - Answer = 2.  If you label the four semicolons a, b, c and d, then the
                               two partitionings are a_b c_____d, and a_c b_____d.

1. ";;;___;;;" - Answer = 36.  Label the semicolons a, b, c, d, e, f, and then underscores x, y, z.
                           
                 axd bye czf - axd bze cyf - ayd bxe czf  - ayd bze cxf - azd bxe cyf  - azd bye cxf
                 axd byf cze - axd bzf cye - ayd bxf cze  - ayd bzf cxe - azd bxf cye  - azd byf cxe
                 axe byd czf - axe bzd cyf - aye bxd czf  - aye bzd cxf - aze bxd cyf  - aze byd cxf
                 axe byf czd - axe bzf cyd - aye bxf czd  - aye bzf cxd - aze bxf cyd  - aze byf cxd
                 axf byd cze - axf bzd cye - ayf bxd cze  - ayf bzd cxe - azf bxd cye  - azf byd cxe
                 axf bye czd - axf bze cyd - ayf bxe czd  - ayf bze cxd - azf bxe cyd  - azf bye cxd

2. "_;__;"                                   - Answer = 0 -- Impossible to partition
3. ";_____________________________________;" - Answer = 1
4. ";__;____;"                               - Answer = 0
5. ";_;;__;_;;"                              - Answer = 52

Testing yourself

Standard tests.sh and answers.txt.

Hints

This looks, feels and smells like a Dynamic Programming problem. The hard part here is spotting the recursion. My first thought was to pick a ";_*;" substring and then make a recursive call on the remaining part of the string. I couldn't get that to work.

Instead, try the following procedure:

long long C(int index, int emoticons_with_underscore, int emoticons_without_underscore);

This procedure will return the count of emoticons given that you have already looked at the first index characters, and you have:

You'll start with C(0,0,0).

Inside C, you'll break your code into three cases:

  1. index is message.size(). In that case, if you have any started emoticons, your answer is zero. If you have no started emoticons, your answer is one. This is the base case.
  2. message[index] is an underscore. If there are no started emoticons, then your answer is zero. However, if you have any started emoticons, then you may assign this underscore to any of them. You'll split this case in two -- first you assign the underscore to a started emoticon that already has underscores. That will result in one recursive call. Second you assign the underscore to a started emoticon that doesn't have underscores. That will result in a second recursive call. You return their sum.
  3. message[index] is an semi-colon. This can be used to start an emoticon (without an underscore), so make that recursive call. Then, if there are emoticons with underscores, this can end one of them. Make that recursive call.
You have to memoize. I did that by converting the three parameters to a string and using a map, although you could use a vector of vectors of vectors of long long's, and simply resize the inner ones on demand. I'm not sure which would be faster.

Let's go through the calls when the string is ";;;___;;;" (Example 1):

C(0,0,0)   = C(1,0,1)                       =   36
C(1,0,1)   = C(2,0,2)                       =   36
C(2,0,2)   = C(3,0,3)                       =   36
C(3,0,3)   = 3*C(4,1,2)                     =   36
C(4,1,2)   = 1*C(5,1,2) + 2*C(5,2,1)        =   12
C(5,1,2)   = 1*C(6,1,2) + 2*C(6,2,1)        =    0
C(5,2,1)   = 2*C(6,2,1) + 1*C(6,3,0)        =    6
C(6,1,2)   = C(7,1,3) + 1*C(7,0,2)          =    0
C(6,2,1)   = C(7,2,2) + 2*C(7,1,1)          =    0
C(6,3,0)   = C(7,3,1) + 3*C(7,2,0)          =    6
C(7,0,2)   = C(8,0,3)                       =    0
C(7,1,1)   = C(8,1,2) + 1*C(8,0,1)          =    0
C(7,1,3)   = C(8,1,4) + 1*C(8,0,3)          =    0
C(7,2,0)   = C(8,2,1) + 2*C(8,1,0)          =    2
C(7,2,2)   = C(8,2,3) + 2*C(8,1,2)          =    0
C(7,3,1)   = C(8,3,2) + 3*C(8,2,1)          =    0
C(8,0,1)   = C(9,0,2)                       =    0
C(8,0,3)   = C(9,0,4)                       =    0
C(8,1,0)   = C(9,1,1) + 1*C(9,0,0)          =    1
C(8,1,2)   = C(9,1,3) + 1*C(9,0,2)          =    0
C(8,1,4)   = C(9,1,5) + 1*C(9,0,4)          =    0
C(8,2,1)   = C(9,2,2) + 2*C(9,1,1)          =    0
C(8,2,3)   = C(9,2,4) + 2*C(9,1,3)          =    0
C(8,3,2)   = C(9,3,3) + 3*C(9,2,2)          =    0
C(9,0,0)   = Base Case.                     =    1
C(9,0,2)   = Base Case.                     =    0
C(9,0,4)   = Base Case.                     =    0
C(9,1,1)   = Base Case.                     =    0
C(9,1,3)   = Base Case.                     =    0
C(9,1,5)   = Base Case.                     =    0
C(9,2,2)   = Base Case.                     =    0
C(9,2,4)   = Base Case.                     =    0
C(9,3,3)   = Base Case.                     =    0

This is fast enough for topcoder, but you could speed it up a bit, by recognizing impossible states, such as C(7,1,3), and returning instantly. That will save on a ton of recursive calls.