https://codeforces.com/contest/1150/problem/D
题意:就是先给一个单词,然后给你三个空串,然后给你一些操作,分别是在单词中取出字母加上一些字母,或者减去一些字母,问三个串有没有冲突的。用过了的字母,在没有别的串减去时是不可以用的。而且一个串的字母的取法是按照他给的操作的顺序的,比如第一次加了a第二次加了c在单词中a的前面的c不能取,只能取a后面的;
做法:不会做。。。。序列自动机+dp,看了官方题解后
dp[i][j][k]表示在他给定的操作下,分别当三个串的长度为,i,j,k的时候,三个串不重复的最小长度,
分别用几个数组进行辅助,d[x][l[x]]表示第x个串长度为l[x]时他给定的抄作的字符,ch[i][j]表示在i之后第一次出现j字母的位置
可以在O(26*N)复杂度内完成;
转移方程如下:
dp[i][j][k] = min(dp[i][j][k], ch[dp[i - 1][j][k]][d[1][i] - 'a']);
dp[i][j][k] = min(dp[i][j][k], ch[dp[i][j - 1][k]][d[2][j] - 'a']);
dp[i][j][k] = min(dp[i][j][k], ch[dp[i][j][k - 1]][d[3][k] - 'a']);
由于可以当前+操作可以进行简化,不用枚举+操作的串,所以时间复杂度为 O(q*250*250)。减操作只需要更新长度即可。
注意dp数组的初始化。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
int n, q,ch[100010][26];
char s[100010], d[5][255];
int l[5], dp[255][255][255];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> q;
cin >> s;
for (int i = 0; i < 26; i++) ch[n][i] = ch[n+1][i] = n + 1;
for (int i = n - 1; i >= 0; i--)
{
int t = s[i] - 'a';
for (int j = 0; j < 26; j++)
ch[i][j] = ch[i + 1][j];
ch[i][t] = i + 1;
}
memset(l, 0, sizeof(l));
memset(dp,0,sizeof(dp));
int x; char t;
for (int p = 1; p <= q; p++)
{
cin >> t >> x;
if (t == '-') l[x]--;
else
{
l[x]++;
cin >> t;
d[x][l[x]] = t;
int i = x == 1 ? l[x] : 0;
for (; i <= l[1]; i++)
{
int j = x == 2 ? l[x] : 0;
for (; j <= l[2]; j++)
{
int k = x == 3 ? l[x] : 0;
for (; k <= l[3]; k++)
{
dp[i][j][k] = n + 1;
if (i&&dp[i - 1][j][k]!=inf)
dp[i][j][k] = min(dp[i][j][k], ch[dp[i - 1][j][k]][d[1][i] - 'a']);
if (j&&dp[i - 1][j][k]!=inf)
dp[i][j][k] = min(dp[i][j][k], ch[dp[i][j - 1][k]][d[2][j] - 'a']);
if (k&&dp[i - 1][j][k]!=inf)
dp[i][j][k] = min(dp[i][j][k], ch[dp[i][j][k - 1]][d[3][k] - 'a']);
}
}
}
}
if (dp[l[1]][l[2]][l[3]] <= n)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
return 0;
}