Codeforces 611D New Year and Ancient Prophecy DP

题意:

把一个字符串分割成多个小串,小串组成严格递增序列,n<=5000

 

这是一个DP。

s代表原串

dp[i][j]代表当前到i位置最后一个串是以j为开头的方案数。答案就是dp[n][1]+...+dp[n][n]

很容易得到dp[i][j]=dp[j-1][k]+dp[j-1][k+1]……dp[j-1][j-1]    (i-j=j-k) 如果s[k-1....j-1] >=s[j....i], dp[i][j]+=dp[j-1][k-1]

这样dp的递推式就有了,很容易发现dp[i][j]的递推式是一个前缀和,假设我比较s[k-1....j-1]和s[j....i]是O(1) 那么就可以在n^2的复杂度里得到答案

所以问题转化为如何预处理以x为开头的串和以y为开头的串的大小(x<y)

令r[x][y]为以x开头的串和以y开头的串经过多少长度分出大小,is[x][y]为true代表x小,为false代表y小

if(r[x-1][y-1]>0) r[x][y]=r[x-1][y-1]-1,is[x][y]=is[x-1][y-1];

else 暴力跑出r[x][y]    

所以对于每一个r[1][y]我们得出r[2][y+1]...r[n-y+1][n]的复杂度是O(n)

所以求出r[1][1]....r[1][n]

然后有了所有的is(x,y) 就可以O(1)比较以x和y开头的子串大小

具体代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=5005;
const long long MOD=1000000007;
long long dp[N][N];
int r[N][N];
bool is[N][N];
char s[N];
char a[N],b[N];
int l1,l2,n;
int main()
{
    int i,j,k;
    while(scanf("%d",&n)!=EOF)
    {
        scanf("%s",s+1);
        for(i=1;i<=n;i++) dp[i][1]=1;
        for(j=2;j<=n;j++)
        {
            is[1][j]=false;
            for(k=0;k+j<=n;k++)
            {
                if(s[1+k]>s[j+k]) {is[1][j]=false;r[1][j]=k;break;}
                if(s[1+k]<s[j+k]) {is[1][j]=true;r[1][j]=k;break;}
            }
            if(k+j>n) r[1][j]=n+1;
        }
        for(k=1;k<n;k++)
        {
            for(i=2;i+k<=n;i++)
            {
                if(r[i-1][i+k-1]) {is[i][i+k]=is[i-1][i+k-1];r[i][i+k]=r[i-1][i+k-1]-1;}
                else
                {
                    is[i][i+k]=false;
                    for(j=0;i+j<=n&&i+k+j<=n;j++)
                    {
                        if(s[i+j]>s[i+k+j]) {is[i][i+k]=false;r[i][i+k]=j;break;}
                        if(s[i+j]<s[i+k+j]) {is[i][i+k]=true;r[i][i+k]=j;break;}
                    }
                    if(i+k+j>n) r[i][i+k]=n+1;
                }
            }
        }
        for(i=2;i<=n;i++)
        {
            k=i;
            if(s[i]=='0') continue;
            long long sum=0;
            for(j=i;j<=n;j++)
            {
               if(k>0) sum+=dp[i-1][k];
               if(sum>=MOD) sum%=MOD;
               k--;
               dp[j][i]+=sum;
               if(k>0&&s[k]=='0') continue;
               int flag=0;
               if(k>0)
               {
                 if(is[k][i]==true&&r[k][i]+k<i) flag=1;
               }
               if(flag) dp[j][i]+=dp[i-1][k];
               if(dp[j][i]>=MOD) dp[j][i]%=MOD;
            }
        }
        long long ans=0;
        for(i=1;i<=n;i++)
        {
            ans+=dp[n][i];
            if(ans>=MOD) ans%=MOD;
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

posted @ 2016-01-02 14:02  Woo95  阅读(237)  评论(0编辑  收藏  举报