• 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;
    }
  • 相关阅读:
    关于Unity中资源打包
    关于Unity中的物理
    关于Unity中特殊目录
    关于Unity中脚本
    千八行
    早春游园
    西湖冬景
    七尖行
    黄山游记
    四季
  • 原文地址:https://www.cnblogs.com/hua-dong/p/11440329.html
Copyright © 2020-2023  润新知