Codeforces 1150D DP

题意:给你一个长度为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\n");
		else printf("NO\n");
	}
} 

  

猜你喜欢

转载自www.cnblogs.com/pkgunboat/p/10902579.html