CodeForces - 1051E :Vasya and Big Integers(Z算法 & DP )
题意:给定字符串S,A,B。现在让你对S进行切割,使得每个切割出来的部分在[A,B]范围内,问方案数。
思路:有方程,dp[i]=Σ dp[j] (S[j+1,i]在合法范围内)。 假设M和N的最长公共前缀为长度是LCP,那么字符串M>=字符串N的条件是 LCP=|N|或者(LCP<|N|&&M[lcp+1]>N[lca+1]); 小于同理。 求出范围就可以用前缀和 O(N)求DP了。
而LCP显然可以用exkmp求。 最近发现Z算法比较好写。 尝试了一下。 这里把两个串连起来一次性求,看起来比较舒服。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; const int Mod=998244353; char s[maxn<<1],l[maxn],r[maxn]; int z1[maxn],z2[maxn],lena,lenl,lenr; void Z(char a[],char t[],int z[]) { int f=strlen(a+1),w=strlen(t+1); a[f+1]='&'; rep(i,1,w) a[f+1+i]=t[i]; int n=f+w+1; z[1]=n; int L=0,R=0; rep(i,2,n) { if(R<i) z[i]=0; else z[i]=min(R-i+1,z[i-L+1]); while(i+z[i]<=n&&a[i+z[i]]==a[z[i]+1]) z[i]++; if(i+z[i]-1>R) L=i,R=i+z[i]-1; } rep(i,1,w) z[i]=z[f+1+i]; } int dp[maxn],sum[maxn]; int main() { scanf("%s%s%s",s+1,l+1,r+1); lena=strlen(s+1); lenl=strlen(l+1); lenr=strlen(r+1); Z(l,s,z1); Z(r,s,z2); dp[lena+1]=sum[lena+1]=1; for(int i=lena;i>=1;i--) { sum[i]=sum[i+1]; if(s[i]=='0') dp[i]=(l[1]=='0')*dp[i+1],sum[i]=(sum[i]+dp[i])%Mod; else { int L=i+lenl,R=min(i+lenr-2,lena); if(z1[i]==lenl||s[i+z1[i]]>l[z1[i]+1]) L--; if(R<lena&&(z2[i]==lenr||s[i+z2[i]]<r[z2[i]+1])) R++; if(L<=R) dp[i]=(sum[L+1]-sum[R+2]+Mod)%Mod,sum[i]=(sum[i]+dp[i])%Mod; } } printf("%d\n",dp[1]); return 0;
}
It is your time to fight!