题意:给你一个长度为n的字符串,有q次询问,每次询问会给字符串x的末尾添加一个字符y,或者删除字符串x末尾的字符,询问过后,要判断长度为n的字符串中是否有3个不重合的子序列,是这3个字符串。
思路:设dp[i][j][j]为3个字符串的长度分别为i, j, k时,匹配的最靠前的位置。那么就枚举是通过哪个字符串转移即可,但是这样是O(n ^ 3)的。我们注意到每次询问过后某个字符串的长度最多改变1,那么我们可以只重新计算没改变的那两维就可以了。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 100010; int Next[maxn][26], dp[255][255][255], now[26]; int a[4][300], tot[4]; char s[maxn]; int main() { int n, m, x; char op[5]; scanf("%d%d", &n, &m); scanf("%s",s + 1); for (int i = 0; i < 26; i++) { now[i] = n + 1; Next[n + 1][i] = n + 1; } for (int i = n; i >= 1; i--) { for (int j = 0; j < 26; j++) { Next[i][j] = now[j]; } now[s[i] - 'a'] = i; } for (int i = 0; i < 26; i++) Next[0][i] = now[i]; while(m--) { scanf("%s", op + 1); if(op[1] == '+') { scanf("%d", &x); scanf("%s", op + 1); int y = op[1] - 'a'; a[x][++tot[x]] = y; for (int i = (x == 1 ? tot[1] : 0); i <= tot[1]; i++) for (int j = (x == 2 ? tot[2] : 0); j <= tot[2]; j++) for (int k = (x == 3 ? tot[3] : 0); k <= tot[3]; k++) { dp[i][j][k] = n + 1; } for (int i = (x == 1 ? tot[1] : 0); i <= tot[1]; i++) for (int j = (x == 2 ? tot[2] : 0); j <= tot[2]; j++) for (int k = (x == 3 ? tot[3] : 0); k <= tot[3]; k++) { if(i) dp[i][j][k] = min(dp[i][j][k], Next[dp[i - 1][j][k]][a[1][i]]); if(j) dp[i][j][k] = min(dp[i][j][k], Next[dp[i][j - 1][k]][a[2][j]]); if(k) dp[i][j][k] = min(dp[i][j][k], Next[dp[i][j][k - 1]][a[3][k]]); } } else { scanf("%d", &x); tot[x]--; } if(dp[tot[1]][tot[2]][tot[3]] <= n) printf("YES "); else printf("NO "); } }