题意:
给一个1e5的串str,然后有三个起始空串,不超过1000次操作,对三个字符串的一个尾部加一个字符或者减一个字符,保证每个字符不会超过250
每次操作之后询问你这三个串是不是可以组成str的子序列,
比如ab,cd,ef可以组成acgbdef的子序列
思路:
dp[i][j][k]为三个串分别匹配到i,j,k的时候组成的子序列的最后一个字符在str中的位置,如果dp[strlen(str1)][strlen(str2)][strlen(str3)]>n则输出no
对每一次询问我们可以处理出dp[i][j][k],
用序列自动机加速,nx[i][j]表示str中i位置的下一个字符j出现在什么位置(不包括i),没出现则为n+1
则状态转移方程
if(i)dp[i][j][k] = min(dp[i][j][k],nx[dp[i-1][j][k]][v[1][i-1]-'a']);
if(j)dp[i][j][k] = min(dp[i][j][k],nx[dp[i][j-1][k]][v[2][j-1]-'a']);
if(k)dp[i][j][k] = min(dp[i][j][k],nx[dp[i][j][k-1]][v[3][k-1]-'a']);
注意序列自动机要同样处理处nx[0][j]和nx[n+1][j],因为状态转移方程中可能会出现
复杂度O(250^3)
但这一题有q次询问,而且三个串在不停改变,我们不能每次都250^3处理,
当操作为“+”的时候,如果添加的是str1,若str1的长度由m变为m+1,可以发现,dp方程改变的只有dp[m+1][j][k],而其他的都没有改变(因为只是在尾部操作)
所以我们对每一次询问可以O(250^2)更新dp数组
而当操作为"-"的时候,我们并不需要更新dp数组,因为我们只能用到dp[m-1][j][k],而这之前已经处理好了,我们下一次“+”的时候,自然会覆盖dp[m][j][k]
所以总的复杂度为$O(26n+250^2q)$
代码:
题解的写法学会了✔
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<string> #include<stack> #include<queue> #include<deque> #include<set> #include<vector> #include<map> #include<functional> #define fst first #define sc second #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define lson l,mid,root<<1 #define rson mid+1,r,root<<1|1 #define lc root<<1 #define rc root<<1|1 #define lowbit(x) ((x)&(-x)) using namespace std; typedef double db; typedef long double ldb; typedef long long ll; typedef long long LL; typedef unsigned long long ull; typedef pair<int,int> PI; typedef pair<ll,ll> PLL; const db eps = 1e-6; const int mod = 1e9+7; const int maxn = 1e6+100; const int maxm = 2e6+100; const int inf = 0x3f3f3f3f; const db pi = acos(-1.0); int n, q; int dp[333][333][333]; vector<char>v[4]; int nx[maxn][26]; char str[maxn]; void init(){ for(int i = 0; i < 26; i++){ nx[n][i] = n+1; nx[n+1][i]=n+1; } for(int i = n-1; i >= 0; i--){ for(int j = 0; j < 26; j++){ if(str[i+1]-'a'==j){ nx[i][j] = i+1; } else nx[i][j] = nx[i+1][j]; } } }int mi[4],mx[4]; int main(){ scanf("%d %d" ,&n, &q); scanf("%s",str+1); init(); while(q--){ char op[3]; scanf("%s",op+1); int x; scanf("%d",&x); if(op[1]=='+'){ char gao[3]; scanf("%s",gao+1); char c = gao[1]; v[x].pb(c); for(int i = 1; i <= 3; i++){ mi[i] = 0; mx[i] = v[i].size(); } mi[x] = mx[x]; for(int i = mi[1]; i <= mx[1]; i++){ for(int j = mi[2]; j <= mx[2]; j++){ for(int k = mi[3]; k <= mx[3]; k++){ if(i+j+k==0)continue; dp[i][j][k] = n+1; if(i)dp[i][j][k] = min(dp[i][j][k],nx[dp[i-1][j][k]][v[1][i-1]-'a']); if(j)dp[i][j][k] = min(dp[i][j][k],nx[dp[i][j-1][k]][v[2][j-1]-'a']); if(k)dp[i][j][k] = min(dp[i][j][k],nx[dp[i][j][k-1]][v[3][k-1]-'a']); } } } } else{ v[x].pop_back(); } if(dp[v[1].size()][v[2].size()][v[3].size()]<=n){ printf("YES "); } else printf("NO "); } return 0; } /* */