CodeForces

题意:给定字符串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
",dp[1]);
    return 0;
}
原文地址:https://www.cnblogs.com/hua-dong/p/11440329.html