# s Answer - --------------------- ------------------- 0 "()[]" 3: Delete indices {0,1}, delete indices {2,3}, delete indices {}. 1 "())" 2: Delete indices {1}, delete indices {2}. Both result in "()". 2 "()()" 4 3 "([)]" 2 4 "())[]][]([]()]]()]]]" 3854
The inefficient solution is to use a power set enumeration, straight out of the CS302 lecture notes. I've written this up in BSPS.cpp. Obviously, this will start to choke when the string's size is in the 20's. But again, it lets us test our answers:
/* Test to see if a string s is a correct bracket sequence. Use a deque like a stack -- if a character is an opening character, push it on the stack. If it is a closing character, check to see its opening character is on the stack. If so, pop it and continue. Otherwise, return false. */ int is_cbs(string &s) { deque <char> stack; int i; for (i = 0; i < s.size(); i++) { if (s[i] == '(' || s[i] == '[') { stack.push_front(s[i]); } else if (s[i] == ')' || s[i] == ']') { if (stack.empty() || (s[i] == ')' && stack[0] != '(') || (s[i] == ']' && stack[0] != '[')) return 0; stack.pop_front(); } else return 0; } return stack.empty(); } int main(int argc, char **argv) { string s, tmp; int i, j, counter; if (argc != 2) { fprintf(stderr, "Usage a.out string\n"); exit(1); } s = argv[1]; counter = 0; /* Generate all non-empty subsets of S and test them. Count up the results. */ for (i = 1; i < (1 << s.size()); i++) { tmp = ""; for (j = 0; j < s.size(); j++) { if (i & (1 << j)) tmp.push_back(s[j]); } if (is_cbs(tmp)) counter++; } printf("%d\n", counter-1); exit(0); } |
s = "[(])][]"Let's enumerate the solutions -- there are 11.
In this problem, as with many dynamic programming problems,
it's best to work from left to right, concentrating on the first
character of the string. So, in the case of our example, we are going
to concentrate on the first character -- the left square brace:
[(])][]
The number of ways to erase characters and get valid bracketed strings is going to
be the number of ways that you can do it by erasing the first character, plus the
number of ways that you can do it by not erasing the first character.
If we erase the first character, than we can simply call count() on the remaining
characters, and that will be our answer. In the example above, we'd call:
count("(])][]");
That's nice. Now, when we don't erase the first character, we need to do something
so that we can recurse on smaller strings. Let's match the first with its nearest
closing square bracket. We get the following (and I'm coloring the strings for clarity):
[(])][]
The number of ways to erase characters and get correct bracket sequences, when you are not
erasing the red characters, will be the number of ways for:
[(]
when you don't delete the red characters, multiplied by
)][]
when you are allowed to delete any and all characters. This turns out to be:
count("(")+1
multiplied by:
count(")][]")+1
Why the +1's? Because we want to count empty strings in these instances, and
count() does not include the empty string. So you add one.
It is important to realize that because you
are not deleting the red characters, it's ok for the other strings to become empty,
and that's why you add one to the recursive count() calls.
Next, you match the first character with the next closing square brace:
[(])][]
And you do the same thing -- it equals
(count("(])")+1)*(count("[]")+1)
Finally, you match the first character with the last closing square brace:
[(])[]
And you do the same thing -- it equals
(count("(])][")+1)*(count("")+1)
Add them up, and you're done. Let's work through the numbers in our example:
Obviously, you'll have to memoize the recursion to make sure you don't have exponential blowup.
/* Topcoder SRM 686, D1, 250-pointer: BracketSequenceDiv1 * James S. Plank * Tue Oct 11 00:57:32 EDT 2016 */ #include <string> #include <vector> #include <list> #include <cmath> #include <algorithm> #include <map> #include <set> #include <iostream> #include <cstdio> #include <cstdlib> using namespace std; class BracketSequenceDiv1 { public: map <string, long long> Cache; long long count(string s); }; long long BracketSequenceDiv1::count(string s) { int i; long long one; long long b, e; map <string, long long>::iterator mit; /* Base case -- empty string equals 0 */ if (s.size() == 0) return 0; /* Memoization looks at the cache first. */ if (Cache.find(s) != Cache.end()) return Cache[s]; /* Set one to be the answer when you erase the first character. If the first character is a closing brace, that's all you can do, so return the answer in that case. */ one = count(s.substr(1)); if (s[0] == ')' || s[0] == ']') { Cache[s] = one; return one; } /* Otherwise, for each closing brace/parenthesis that matches the first character, split the string in two at that point, and recurse on the first half and the second half, adding one to account for empty strings. Add in the products. */ for (i = 1; i < s.size(); i++) { if ((s[0] == '(' && s[i] == ')') || (s[0] == '[' && s[i] == ']')) { b = count(s.substr(1, i-1))+1; e = count(s.substr(i+1))+1; one += (b*e); } } /* At the end, put the value into the cache, and return. */ Cache[s] = one; return one; } |