CodeForces 611 D. New Year and Ancient Prophecy(dp)

Description
给出一个长度为n的数字串,要求将这个数字串拆成一些无前置零的子串,使得这些子串所表示的数字是严格递增的,问有多少种拆分方法
Input
第一行为一整数n表示数字串长度,之后为一数字串(1<=n<=5000)
Output
输出拆分方案,结果模1e9+7
Sample Input
6
123434
Sample Output
8
Solution
以dp[i][j]表示以第i个字符结尾且最后一个子串长度为j的划分种类数,那么
dp[i][j]=dp[i][j]+sum(dp[i-j][k])+strcmp([i-2*j+1,i-j],[i-j+1,i])<0?dp[i-j][j]:0 (1<=k< j)
而直接比较子串[i-2*j+1,i-j]和子串[i-j+1,i]的大小时间代价太高,所以可以预处理出以第i个字符开头和以第j个字符开头的子串最长公共前缀的长度lcp[i][j](轻松得到递推方程lcp[i][j]=s[i]==s[j]?lcp[i+1][j+1]+1:0),那么如果len< j&&s[i-2*j+1+len]< s[i-j+1+len](其中len=lcp[i-2*j+1][i-j+1]),那么第三项就可以加上。最终答案ans=sum(dp[n][i])(1<=i<=n)
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define maxn 5555
#define mod 1000000007ll
typedef long long ll;
int n,lcp[maxn][maxn],dp[maxn][maxn],sum[maxn][maxn];
char s[maxn];
void get_lcp()
{
    for(int i=n;i>0;i--)
        for(int j=n;j>0;j--)
            if(s[i]==s[j])lcp[i][j]=lcp[i+1][j+1]+1;
            else lcp[i][j]=0;
}
int main()
{
    while(~scanf("%d%s",&n,s+1))
    {
        memset(sum,0,sizeof(sum));
        memset(dp,0,sizeof(dp));
        get_lcp();
        for(int i=1;i<=n;i++)dp[i][i]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                if(s[i-j+1]=='0')continue;
                dp[i][j]=(dp[i][j]+sum[i-j][j-1])%mod;
                int len=lcp[i-2*j+1][i-j+1];
                if(i-2*j+1>=1&&len<j&&s[i-2*j+1+len]<s[i-j+1+len])
                    dp[i][j]=(dp[i][j]+dp[i-j][j])%mod;
            }
            for(int j=1;j<=n;j++)
                sum[i][j]=(sum[i][j-1]+dp[i][j])%mod;
        }
        ll ans=0;
        for(int i=1;i<=n;i++)ans=(ans+dp[n][i])%mod;
        printf("%I64d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(CodeForces 611 D. New Year and Ancient Prophecy(dp))