Codeforces 611d [DP][字符串]

/*
题意:给一个长度不超过5000的字符串,每个字符都是0到9的数字。
要求将整个字符串划分成严格递增的几个数字,并且不允许前导零。
思路:
1.很开心得发现,当我在前i个区间以后再加一个区间的时候,转移
的条件只跟最后一个区间的数字大小有关,这决定这道题可以dp...
2.dp[i][j]代表前j个字符,最后划分的区间的第一个字符是第i个的答案数。
3.可知对于所有的dp[i][i...n]他们的答案都取决与dp[1...i-1][i-1].
4.很容易想到对于同一个i,累加求得dp[i][k]的值,但是如何判断
数字大小呢...首先长度不同的情况下直接根据长度判断即可(因为不允许
前导零)。长度相同的情况下,需要比较这两个字符串哪个大。这个时候
预处理出  ook[i][j]代表以第i个位置和第j个位置为开头的两个字符串他嗯
第一个不相同的字符的位置。


*/








// 2016/9/6 13:07
#include<bits/stdc++.h>
#define N 5005
using namespace std;
int ook[5005][5005];
long long dp[N][N];
long long mod=1e9+7;
char jilu[N];
int main()
{
    int n;
    scanf("%d%s",&n,jilu+1);
    memset(ook,0x3f,sizeof(ook));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(jilu[i]!=jilu[j]){
                int k=0;
                while(i-k>0&&j-k>0&&ook[i-k][j-k]>k){
                    ook[i-k][j-k]=k;
                    k++;
                }
            }
        }
    }
    for(int i=1;i<=n;i++){
        if(jilu[i]=='0')continue;
        long long sum=0;
        int a=i-1,b=i;
        while(b<=n){
            if(i==1){
                dp[i][b]=1;
            }
            else{
                while(a>0&&i-a<b-i+1){
                    sum+=dp[a][i-1];
                    sum%=mod;
                    a--;
                }
                if(a>0&&ook[a][i]<=b-i&&jilu[a+ook[a][i]]<jilu[i+ook[a][i]]){
                    sum+=dp[a][i-1];
                    sum%=mod;
                    a--;
                }
                dp[i][b]=sum;
            }
            b++;
        }
    }
    long long ans=0;
    for(int i=1;i<=n;i++){
        ans+=dp[i][n];
        ans%=mod;
    }
    printf("%lld
",ans);
}
原文地址:https://www.cnblogs.com/tun117/p/5846076.html