This is the first activity of Computerization algorithm team. We discussed a problem from Shanghai NOI regional selection, which leverages state compression dynamic programming. The mathematics is beyond our capacity to be rigorously proven.
Problem
Given a string composed of the first lower-case letters, is a factorial string if all the permutations of the first lower-case letters (there are of them)(共种)are subsequences (not necessarily consecutive) of .
Given this definition, we can easily verify if a sequence is factorial by bashing, but it's too slow. Please design an algorithm that verifies if a string is factorial in 1 second.
Input constraints | Memory limit | Execution time |
---|---|---|
, | 125MB | 1.0s |
Solution
The bashing solution is obviously enumerating all permutations and checking if they are subsequences. You get 30% of the score from this method, but the program is pretty meaningless, and the factorial time complexity prohibits further optimization, so we don't discuss further on this attempt.
Now consider the rest 70% marks. For we may consider an algorithm. Since a full enumeration is not possible, we shall turn to state compression dynamic programming, and enumerate if a letter exists in the state. Since we need to find if all permutations of the first letters exist in , we will keep track of the earliest occurence of in .
The state transfer equation:
But if every lookup is done by a loop, we would exceed the time limit and only get 50 points, so we need some preprocessing. We can conduct binary search after preprocessing, but for the sake of convenience, it may be better to maintain another array that marks the position of the next target letter at every position. The time for this is just .
Now, consider the remaining marks. We notice that for large enough , we can just output "No"
. But how do we determine the exact threshold? If the construction is like ,, the length of is about ,, but we weren't able to prove it rigorously. To be on the safer side, because the runtime is still acceptable for , we choose to output "No"
for .
Program
#include <iostream>
using namespace std;
int main(){
int t;
cin >> t;
while (t--) {
int n;
cin >> n;
string s;
cin >> s;
if (n >= 23) {
cout << "NO" << endl;
} else {
short NEXT[26][450];
for (int i = s.length() - 1; i >= 0; i--) {
if (i == s.length() - 1) {
for (int j = 0; j < 26; j++)
NEXT[j][i] = -1;
} else {
for (int j = 0; j < 26; j++)
NEXT[j][i] = NEXT[j][i + 1];
}
NEXT[s[i] - 'a'][i] = i;
}
short *f = new short[1 << n];
for (int i = 0; i < (1 << n); i++) {
f[i] = 0;
for (int j = 0; (1 << j) <= i; j++) {
if (i & (1 << j)) {
if (NEXT[j][f[i - (1 << j)]] == -1) {
cout << "NO" << endl;
return 0;
}
f[i] = max(f[i], NEXT[j][f[i - (1 << j)]]);
}
}
}
cout << "YES" << endl;
}
}
}