题目大意
给出长度为n(n<=100000)n(n<=100000)n(n<=100000)的小写字母字符串sss,一开始有三个空串t1,t2,t3t_1,t_2,t_3t1,t2,t3,有q(q<=1000)q(q<=1000)q(q<=1000)次操作,每次操作对这三个字符串中某一个末尾添上一个小写字母或者删掉末尾的字母,每次操作之后进行一次询问,能否将这三个字符串作为sss中不相交的子序列。(保证任何时候t1,t2,t3t_1,t_2,t_3t1,t2,t3长度都不超过250)
题解
设dp[i][j][k]dp[i][j][k]dp[i][j][k]表示对三个串的指针分别走到i,j,ki,j,ki,j,k时在s中对应的最短前缀的长度,如果不能匹配则为n+1n+1n+1。这样对每次操作之后更新一次dp,只需要判断当前的dp值是否超过n即可。因为每次只更新一个串,所以每次操作只需要O(2502)O(250^2)O(2502),总复杂度则为O(q∗2502)O(q*250^2)O(q∗2502),其中可以加入nxt数组来加速,nxt[i][j]nxt[i][j]nxt[i][j]表示在sss中第iii个字母之后的第一个字母jjj所在的位置,如果不存在则为n+1n+1n+1。
代码如下
#include<bits/stdc++.h>
using namespace std;
int n,q,len[5],dp[255][255][255],nxt[100005][30];
char s[100005],t[5],d[5][255];
int main(){
scanf("%d%d%s",&n,&q,s+1);
for(int i=0;i<26;i++)nxt[n][i]=nxt[n+1][i]=n+1;
for(int i=n-1;i>=0;i--){
for(int j=0;j<26;j++){
nxt[i][j]=nxt[i+1][j];
}nxt[i][s[i+1]-'a']=i+1;
}
for(int x,i=1;i<=q;i++){
scanf("%s%d",t,&x);
if(*t=='-'){
len[x]--;
}else{
len[x]++;
scanf("%s",t);
d[x][len[x]]=*t;
for(int p1=x==1?len[x]:0;p1<=len[1];p1++){
for(int p2=x==2?len[x]:0;p2<=len[2];p2++){
for(int p3=x==3?len[x]:0;p3<=len[3];p3++){
dp[p1][p2][p3]=n+1;
if(p1)dp[p1][p2][p3]=min(dp[p1][p2][p3],nxt[dp[p1-1][p2][p3]][d[1][p1]-'a']);
if(p2)dp[p1][p2][p3]=min(dp[p1][p2][p3],nxt[dp[p1][p2-1][p3]][d[2][p2]-'a']);
if(p3)dp[p1][p2][p3]=min(dp[p1][p2][p3],nxt[dp[p1][p2][p3-1]][d[3][p3]-'a']);
}
}
}
}puts(dp[len[1]][len[2]][len[3]]<=n?"YES":"NO");
}
return 0;
}