Dynamic programming brute force reduction

An emoticon consists of an arbitrary positive number of underscores between two semicolons. Therefore, the shortest emoticon ;_;. Lines ;__;and ;_____________;are also valid emoticons.

given a string containing only ( ;, _). The task is to split the line into one or more emoticons and calculate how many sections are possible. Each emoticon must be a subsequence of the message, and each message symbol must belong to exactly one emoticon. Note that subsequences do not have to be contiguous. definition of subsequence .

The approach I was thinking about is to write a recursive method as follows:

countDivision(string s){
 //base cases
 if(s.empty()) return 1;
 if(s.length()<=3){
   if(s.length()!=3) return 0;
   return s[0]==';' && s[1]=='_' && s[2]==';';
 }
  result=0;
//subproblems
  genrate all valid emocticon and remove it from s let it be w
  result+=countDivision(w);
  return result;
}

The solution given above will be easily timed out when n is large, for example 100. What approach should be used to convert this brute force solution into a dynamic programming solution?

A few examples

 1. ";_;;_____;" ans is 2
 2. ";;;___;;;" ans is 36

Example 1.

 ";_;;_____;" Returns: 2 
 There are two ways to divide this string into two emoticons. 
 One looks as follows:  ;_;|;_____; and the other looks like
 this(rembember we can pick subsequence it need not be contigous):  ;_ ;|; _____;
+4
source share
2 answers

I will describe the O (n ^ 4) -time solution and -dimensional dynamic programming (which can be easily improved to use only the O (n ^ 3) space), which should work up to n = 100 or so.

A subsequence call is “fresh” if it consists of one ;.

"", .

"", . (, ;, ;_ ;___ - , _, ;; ;___;; - .)

, "", , .

f (i, j, k, m) - j + k + m , j , k - m . , i, j, k m - , (i, j, k, m), , (i, j, k, m) , , . , 1 <= j <= n of f (n, 0, j, 0).

If s[i] = "_":
  f(i, j, k, m) =
    (j+1) * f(i-1, j+1, k, m-1)    // Convert any of the j+1 fresh subsequences to partial
    + m * f(i-1, j, k, m)          // Add _ to any of the m partial subsequences

Else if s[i] = ";":
  f(i, j, k, m) =
    f(i-1, j-1, k, m)              // Start a fresh subsequence
    + (m+1) * f(i-1, j, k-1, m+1)  // Finish any of the m+1 partial subsequences

f(0, 0, 0, 0) = 1
f(0, _, _, _) = 0
f(i, j, k, m) = 0 if any of i, j, k or m are negative

++ 36 ;;;___;;; , , ;;;___;;;_;_; 540 ( ). , 66 ;, 66 _, 66 ; s, 2 0 (, - long long).

+2

memoized , 66 ;, 66 _, 66 ; s. : i = index in the string, j = number of accumulating emoticons with only a left semi-colon k = number of accumulating emoticons with a left semi-colon and one or more underscores.

, , .

O(n^3), , j n/2 k n/4.

JavaScript:

var s = ';_;;__;_;;';

// record the number of semi-colons and 
// underscores to the right of each index
var cs = new Array(s.length);
cs.push(0);

var us = new Array(s.length);
us.push(0);

for (var i=s.length-1; i>=0; i--){
  if (s[i] == ';'){
    cs[i] = cs[i+1] + 1;
    us[i] = us[i+1];
    
  } else {
    us[i] = us[i+1] + 1;
    cs[i] = cs[i+1];
  }
}

// memoize
var h = {};

function f(i,j,k){
  // memoization
  var key = [i,j,k].join(',');

  if (h[key] !== undefined){
    return h[key];
  }

  // base case
  if (i == s.length){
    return 1;
  }

  var a = 0,
      b = 0;
  
  if (s[i] == ';'){
    // if there are still enough colons to start an emoticon
    if (cs[i] > j + k){  
      // start a new emoticon
      a = f(i+1,j+1,k);
    }
      
    // close any of k partial emoticons
    if (k > 0){
      b = k * f(i+1,j,k-1);
    }
  } 
  
  if (s[i] == '_'){
    // if there are still extra underscores
    if (j < us[i] && k > 0){
      // apply them to partial emoticons
      a = k * f(i+1,j,k);
    }
    
    // convert started emoticons to partial
    if (j > 0){
      b = j * f(i+1,j-1,k+1);
    }
  }
  
  return h[key] = a + b;
}

console.log(f(0,0,0)); // 52
Hide result
+1

Source: https://habr.com/ru/post/1654777/