【ZJ选讲·BZOJ 5073】
小A的咒语
给出两个字符串A,B (len<=105)
现在可以把A串拆为任意段,然后取出不超过 x 段,按在A串中的前后顺序拼接起来
问是否可以拼出B串。
【题解】
①如果遇上dp空间会炸,可以将那一维状态存入f[]中
②f[i][j]表示A串1~i位已经取出了x段,能够匹配B的最远位置的B的下标。
③没有后效性,因此DP是合理的:令l=lcp(A[i+1],B[dp[i][j]+1(后缀数组RMQ维护)
dp[i+1][j]=max(dp[i+1][j],dp[i][j]) 【不包含i】
dp[i+l][j+1]=max(dp[i+l][j+1],dp[i][j]+l) 【包含i】
#include<cstdio> #include<cstring> #include<iostream> #define MAXN 100005 using namespace std; char s[MAXN*2]; int sa[MAXN*2],rk[MAXN*2],ht[MAXN*2]; int dp[MAXN][105],st[MAXN*2][20],log2[MAXN*2]; void print(int n){ puts("sa:"); for(int i=0;i<n;i++) printf("%d ",sa[i]); puts(""); puts("rk:"); for(int i=0;i<n;i++) printf("%d ",rk[i]); puts(""); puts("ht:"); for(int i=0;i<n;i++) printf("%d ",ht[i]); puts(""); } bool cmp(int *y,int len,int k,int p1,int p2){ int a=y[p1],b=y[p2]; int aa=p1+k<len?y[p1+k]:-1,bb=p2+k<len?y[p2+k]:-1; return a==b&&aa==bb; } void build(int n,int m){ static int wa[MAXN*2],wb[MAXN*2],c[MAXN*2],*x,*y,p; x=wa; y=wb; for(int i=0;i<m;i++) c[i]=0; for(int i=0;i<n;i++) c[x[i]=s[i]]++; for(int i=1;i<m;i++) c[i]+=c[i-1]; for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i; for(int k=1;k<n;k<<=1){ p=0; for(int i=n-k;i<n;i++) y[p++]=i; for(int i=0;i<n;i++) if(sa[i]-k>=0) y[p++]=sa[i]-k; for(int i=0;i<m;i++) c[i]=0; for(int i=0;i<n;i++) c[x[y[i]]]++; for(int i=1;i<m;i++) c[i]+=c[i-1]; for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; m=1; swap(x,y); x[sa[0]]=0; for(int i=1;i<n;i++) x[sa[i]]=cmp(y,n,k,sa[i-1],sa[i])?m-1:m++; if(m>=n) break; } for(int i=0;i<n;i++) rk[sa[i]]=i; int h=0; for(int i=0,j;i<n;i++){ if(h) h--; if(rk[i]!=0){ j=sa[rk[i]-1]; while(i+h<n&&j+h<n&&s[i+h]==s[j+h]) h++; } ht[rk[i]]=h; } for(int i=1;i<n;i++) st[i][0]=ht[i]; for(int k=1;(1<<k)<n;k++) for(int i=1<<k;i<n;i++) st[i][k]=min(st[i-(1<<(k-1))][k-1],st[i][k-1]); } int lcp(int i,int j){ i=rk[i]; j=rk[j]; if(i>j) swap(i,j); i++; int k=log2[j-i+1]; return min(st[i+(1<<k)-1][k],st[j][k]); } int main(){ log2[1]=0; for(int i=2;i<200000;i++) log2[i]=log2[i>>1]+1; int T; scanf("%d",&T); while(T--){ int n,m,x; bool fg; memset(dp,0,sizeof(dp)); scanf("%d%d%d",&n,&m,&x); scanf("%s",s); s[n]='%'; scanf("%s",s+n+1); fg=0; build(n+m+1,301); for(int i=-1;i<n;i++) for(int j=0;j<=min(i+1,x);j++){ int d=i>=0?dp[i][j]:0; if(d==m) fg=1; dp[i+1][j]=max(dp[i+1][j],d); int l=(i+1<n&&n+1+d<n+m+1)?lcp(i+1,n+1+d):0; if(l==0) continue; dp[i+l][j+1]=max(dp[i+l][j+1],d+l); } if(fg) printf("YES\n"); else printf("NO\n"); } return 0; }//*ZJ
.