lcp+dp Codeforces611D New Year and Ancient Prophecy

传送门:点击打开链接

题意:长为n的只有数字组成的字符串(n<=5000),问能分割成多少组数字,这些数字里不含前导0,且数字的大小满足严格单调递增

思路:最难的地方,就是如何去快速判断两个数字的大小谁大谁小呢?

我们先来讲下最长公共前缀lcp的定义。如果有串A和串B,lcp[i][j]表示的是串A从原串第i位置开始,串B从原串第j位置开始,那么从这两个位置开始的有多少个字符相等

那么如何来求lcp呢,方程很简单,看了都能懂

lcp[i][j]=lcp[i+1][j+1]+1,if(s[i]==s[j])

lcp[i][j]=0,if(s[i]!=s[j])


求出lcp后如何快速判断两个区间的大数字是否相等呢?

假如lcp[a][b]>=len ,说明两个区间的数字是完全相等的,此时肯定数字是相等的

如果lcp[a][b]<len,那么我们只需要比较s[a+lcp[a][b]]和s[b+lcp[a][b]]的大小就可以了,因为这个位置是两个子串第一个不一样的位置.

知道了这个的话,我们就能再来考虑这道题的dp了。


设dp[i][j](j<=i)表示现在只考虑前i个,最后一个数字是以第j个开头。

那么就能得到转移方程

dp[i][j]+=dp[j-1][k], max(j+1-len,1)<=k<=j-1

dp[i][j]+=dp[j-1][j-len],如果j-len>=1且以j-len开头比以j开头且长度为len数字要小

边界条件是j=1,此时应该等于1


感觉还有地方,,就是写dp的时候总是处理不好边界条件

其实感觉如果就在第二层for里面写if判断边界,这是一种非常好的方法,减少了很多思考,。

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;
typedef pair<int, int>PII;

const int MX = 5e3 + 5;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;

char s[MX];
short lcp[MX][MX];
int n, dp[MX][MX], pre[MX][MX];

bool check(int a, int b, int len) {
    int t = lcp[a][b];
    if(t < len && s[a + t] < s[b + t]) return true;
    return false;
}

void solve() {
    memset(lcp, 0, sizeof(lcp));
    memset(pre, 0, sizeof(pre));
    memset(dp, 0, sizeof(dp));
    for(int i = n; i >= 1; i--) {
        for(int j = n; j >= 1; j--) {
            if(s[i] == s[j]) lcp[i][j] = lcp[i + 1][j + 1] + 1;
            else lcp[i][j] = 0;
        }
    }

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= i; j++) {
            if(s[j] == '0') continue;
            if(j == 1) dp[i][j] = 1;

            int len = i - j + 1;
            int l = max(j + 1 - len, 1), r = j - 1;
            if(l <= r) dp[i][j] = (pre[j - 1][r] - pre[j - 1][l - 1]) % mod;
            if(j - len >= 1 && check(j - len, j, len)) {
                dp[i][j] = (dp[i][j] + dp[j - 1][j - len]) % mod;
            }
        }
        for(int j = 1; j <= i; j++) {
            pre[i][j] = (pre[i][j - 1] + dp[i][j]) % mod;
        }
    }

    int ans = 0;
    for(int i = 1; i <= n; i++) {
        ans = (ans + dp[n][i]) % mod;
    }
    printf("%d\n", (ans + mod) % mod);
}

int main() {
    //FIN;
    while(~scanf("%d%s", &n, s + 1)) {
        solve();
    }
    return 0;
}


你可能感兴趣的:(lcp+dp Codeforces611D New Year and Ancient Prophecy)