题意 给你两个串s,t和一个数k,询问是否存在s的k个不重叠子串按原顺序排列后能组成 t,
数据范围 $|s|<1e5$,$|t|<1e5$,$k<100$,时间30s
题解 设状态$dp1[i][j]$代表串$s$从$s[1]$匹配到$s[i]$的时候选择了$k$个不重叠子串后,能够匹配到t串到最远位置。
设状态$dp2[i][j]$代表串s从$s[1]$匹配到$s[i]$的时候选择了$k$个不重叠子串且最后一个子串以$s[i]$结尾后,能够匹配到t串到最远位置。
根据定义$dp1[i][j]=max(dp2[t][j]), \forall t<=i$,于是简单的写出了转移式子:$dp1[i][j]=max(dp1[i-1][j],dp2[i][j])$
复杂的是$dp2[i][j]$不好得到,根据定义,$dp2[i][j]$一定会选择$s[i]$作为结尾的,现在分两类来讨论:
$s[i]$单独为一个子串,那么可以由$dp1[i-1][j-1]$转移过来,
$s[i]$可能不单独为一个子串,是跟前面的某串拼在一起的,那么可以由$dp2[i-1][j]$转移过来。
知道了转移来源,但是状态转移过来了,值要怎么更新呢?处理不好的话,更新是个麻烦事,这里我们预处理出$t$串里面从$t[1]$到$t[i]$中字符$ch$最后一次出现的位置,成为$max_index_before[i][ch]$
于是我们就有了转移式:
1 2 3 4 dp2[i][j]=max( max_index_before[dp1[i-1][j-1]+1][s[i]], max_index_before[dp2[i-1][j]+1][s[i]] );
最后处理一下边界情况,把输入进来的s变成"@&"+s
,把t变成"@"+t
,k增大1,边界就简单了。最终答案在$dp1[n][k]$中。
时间复杂度O(nk)
做完了之后翻了下题解,没有人用我这方法做这个题目。。。。。。
正解如下:
考虑$dp[i][j]$就是我上文所谈到的$dp1[i][j]$,他没有$dp2[i][j]$,所以不能像我那样转移了。用到了“我为人人”的转移思路,$dp[i][j]$如果转移走的时候花费一次取子串,就能够转移到$dp[i+len][j+1]$,其中len代表lcp(s.suffix(i+1),t.suffix(dp[i][j]+1))
,这个地方的lcp可以用后缀数组+rmq预处理优化为O(1),如果不花费就转移到了$dp[i+1][j]$。总体上时间复杂度为O(nlogn+nk),时间复杂度较前一个做法偏高,代码也很长。
事实也是如此
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <bits/stdc++.h> using namespace std;const int MAXN=1e5 +555 ;char s[MAXN],t[MAXN];int dp1[MAXN][105 ];int dp2[MAXN][105 ];int max_index_before[MAXN][256 ];int main () { int T,n,m,k; scanf ("%d" ,&T); while (T--){ scanf ("%d%d%d%s%s" ,&n,&m,&k,s+3 ,t+2 ); n+=2 ; m++; k++; s[1 ]=t[1 ]='@' ; s[2 ]='&' ; int max_index[256 ]; for (int i=0 ;i<256 ;i++){ max_index[i]=0 ; } for (int i=1 ;i<=m;i++){ max_index[t[i]]=i; for (int j='a' ;j<='z' ;j++){ max_index_before[i][j]=max_index[j]; } max_index_before[i]['@' ]=max_index['@' ]; max_index_before[i]['&' ]=max_index['&' ]; } for (int i=1 ;i<=n;i++){ dp1[i][1 ]=1 ; dp2[i][1 ]=0 ; } dp2[1 ][1 ]=1 ; for (int j=1 ;j<=k;j++){ dp1[1 ][j]=dp2[1 ][j]=1 ; } for (int i=2 ;i<=n;i++){ for (int j=2 ;j<=k;j++){ dp2[i][j]=max (max_index_before[dp1[i-1 ][j-1 ]+1 ][s[i]],max_index_before[dp2[i-1 ][j]+1 ][s[i]]); dp1[i][j]=max (dp1[i-1 ][j],dp2[i][j]); } } if (dp1[n][k]==m)puts ("YES" ); else puts ("NO" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 #include <bits/stdc++.h> using namespace std;struct SA { static const int MAXN=2e5 +555 ; static int lg[MAXN]; int h[MAXN][20 ],rank[MAXN],sa[MAXN],n; void init (char *s,int len) { static int x[MAXN],y[MAXN],c[MAXN]; n=len; int m=1000 ; for (int i=1 ;i<=n;i++)sa[i]=y[i]=0 ; for (int i=1 ;i<=n;i++)x[i]=s[i]; for (int i=1 ;i<=m;i++)c[i]=0 ; for (int i=1 ;i<=n;i++)c[x[i]]++; for (int i=1 ;i<=m;i++)c[i]+=c[i-1 ]; for (int i=n;i>=1 ;i--)sa[c[x[i]]--]=i; for (int k=1 ;k<=n;k<<=1 ){ int num=0 ; for (int i=n-k+1 ;i<=n;i++)y[++num]=i; for (int i=1 ;i<=n;i++)if (sa[i]>k)y[++num]=sa[i]-k; for (int i=1 ;i<=m;i++)c[i]=0 ; for (int i=1 ;i<=n;i++)c[x[i]]++; for (int i=1 ;i<=m;i++)c[i]+=c[i-1 ]; for (int i=n;i>=1 ;i--)sa[c[x[y[i]]]--]=y[i]; for (int i=1 ;i<=n;i++)y[i]=x[i]; for (int i=1 ;i<=n;i++)x[i]=0 ; num=1 ; x[sa[1 ]]=1 ; for (int i=2 ;i<=n;i++){ if ((y[sa[i]]!=y[sa[i-1 ]])||(y[sa[i]+k]!=y[sa[i-1 ]+k])){ x[sa[i]]=++num; } else x[sa[i]]=num; } if (num>=n)break ; m=num; } for (int i=1 ;i<=n;i++)rank[i]=x[i]; int k=0 ; for (int i=1 ;i<=n;i++){ if (k)k--; int j=sa[rank[i]-1 ]; while ((i+k<=n)&&(j+k<=n)&&(s[i+k]==s[j+k]))k++; h[rank[i]][0 ]=k; } for (int j=1 ;j<20 ;j++){ int d=1 <<j; for (int i=1 ;i+2 *d-1 <=n;i++)h[i][j]=min (h[i][j-1 ],h[i+d][j-1 ]); } if (lg[1 ]!=0 )for (int i=1 ;i<MAXN;i++)lg[i]=trunc (log (i+0.5 )/log (2 )); } int lcp (int x,int y) { int L=min (rank[x],rank[y])+1 ; int R=max (rank[x],rank[y]); int k=lg[R-L+1 ]; return min (h[L][k],h[R-(1 <<k)+1 ][k]); } }sa; int SA::lg[SA::MAXN];const int MAXN=1e5 +555 ;char s[MAXN<<1 ];int dp[MAXN][105 ];int main () { int T,n,m,k; scanf ("%d" ,&T); while (T--){ scanf ("%d%d%d" ,&n,&m,&k); n+=2 ; m++; k++; char *t=s+n; scanf ("%s%s" ,s+3 ,t+2 ); s[1 ]=t[1 ]='@' ; s[2 ]='&' ; sa.init (s,n+m); for (int i=1 ;i<=n;i++){ for (int j=1 ;j<=k;j++){ dp[i][j]=0 ; } } for (int i=1 ;i<=n;i++)dp[i][1 ]=1 ; for (int j=1 ;j<=k;j++)dp[1 ][j]=1 ; for (int i=1 ;i<=n;i++){ for (int j=1 ;j<=k;j++){ dp[i+1 ][j]=max (dp[i+1 ][j],dp[i][j]); if (dp[i][j]>=m)continue ; int len=sa.lcp (i+1 ,n+dp[i][j]+1 ); dp[i+len][j+1 ]=max (dp[i][j]+len,dp[i+len][j+1 ]); } } if (dp[n][k]==m)puts ("YES" ); else puts ("NO" ); }