传送门:点击打开链接
题意:长为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; }