lcp+dp Codeforces611D New Year and Ancient Prophecy

来源:互联网 发布:福州几个打车软件 编辑:程序博客网 时间:2024/06/06 03:36

传送门:点击打开链接

题意:长为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;}


1 0
原创粉丝点击