题意
有一个字符串s,和一个字符串t,问可不可以将t分为前后两个部分,使得每个部分都对应一个s中的子序列,且这两个子序列不相交。
数据范围
字符串总长<=400
解法
首先有一个naive的
解法,设dp状态f[i][j][k]表示考虑到s串的第i个字符,t的前半部分考虑到的位置为j,后半部分考虑到的位置为k的状态是否可行,然后需要枚举前半部分的总长。
考虑优化这个做法,发现dp转移全是01,考虑能否用bitset优化这个做法:首先考虑前半部分的转移,
,可以发现此时与k没什么关系,所以直接bitset就行,然后是后半部分的转移:
这里由于我们需要枚举k,所以这里的转移需要一个额外的bitset记录前面的判别式,具体可以看代码
复杂度
#include<bits/stdc++.h>
using namespace std;
const int maxn=405;
inline int read(){
char c=getchar();int t=0,f=1;
while((!isdigit(c))&&(c!=EOF)){if(c=='-')f=-1;c=getchar();}
while((isdigit(c))&&(c!=EOF)){t=(t<<3)+(t<<1)+(c^48);c=getchar();}
return t*f;
}
int T,n,m;
bitset<maxn> f[maxn][maxn],alfa[maxn];
char s[maxn],t[maxn];
signed main(){
//freopen("5.in","r",stdin);
//freopen("5.out","w",stdout);
T=read();
while(T--){
for(int i=1;i<=n;i++)alfa[i].reset();
scanf("%s",s+1);
scanf("%s",t+1);
n=strlen(s+1);
m=strlen(t+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(s[i]==t[j])alfa[i][j]=1;
}
}
int ans=0;
for(int k=1;k<=m;k++){//这里的k和题解中的k不是一个意思,这里枚举的是前后部分的分界点(题解中的k并没有被枚举)
f[0][0][k]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<=k;j++){
f[i+1][j]|=f[i][j];
if(s[i+1]==t[j+1]){
f[i+1][j+1]|=f[i][j];
}
f[i+1][j]|=(f[i][j]<<1)&(alfa[i+1]);//这里就是转移后面部分的方程
}
}
//printf("%d\n",k);
if(f[n][k][m]==1){
//printf("%d\n",k);
for(int i=0;i<=n;i++){
for(int j=0;j<=k;j++){
f[i][j].reset();
}
}
ans=1;break;
}
for(int i=0;i<=n;i++){
for(int j=0;j<=k;j++){
f[i][j].reset();
}
}
}
if(ans)puts("YES");
else puts("NO");
}
return 0;
}