题意:给你一个数字组成的字符串,把它分成几个子串,使得每个串组成的数,没有前导0,且位置在前的字符串组成的数要严格小于位置在后的字符串,问你有多少种不同的分法。
思路:LCP+dp
转移方程:dp [ i][ j] +=dp[ i-j][k]( 1 <=k <=j-1)用sum数组代表总和。
dp[ i][ j] 表示以第i个位置为结尾的最后一块长度为 j 的字符的分法数,则最后一块的起点是i-j+1,前一块等长度j的起点是i-j+1-j;若lcp[l][r]<j&&s[l+lcp[l][r]]<s[r+lcp[l][r]],则 dp[i][j]=dp[i][j]+dp[i-j][j]。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<iostream> 2 #include<string> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cstdio> 6 #include<set> 7 #include<map> 8 #include<vector> 9 #include<cstring> 10 #include<stack> 11 #include<cmath> 12 #include<queue> 13 #include <bits/stdc++.h> 14 using namespace std; 15 #define INF 0x3f3f3f3f 16 #define ll long long 17 #define clc(a,b) memset(a,b,sizeof(a)) 18 const int maxn=1000000; 19 const int mod=1e9+7; 20 21 int t; 22 int sum[5010][5010]; 23 int dp[5010][5010]; 24 int lcp[5010][5010]; 25 char s[5010]; 26 27 void init() 28 { 29 for(int i=t; i>=1; i--) 30 { 31 for(int j=t; j>i; j--) 32 { 33 if(s[i]==s[j]) 34 lcp[i][j]=1+lcp[i+1][j+1]; 35 else 36 lcp[i][j]=0; 37 } 38 } 39 } 40 int main() 41 { 42 while(~scanf("%d",&t)) 43 { 44 scanf("%s",s+1); 45 clc(sum,0); 46 clc(dp,0); 47 clc(lcp,0); 48 init(); 49 dp[0][0]=1; 50 for(int i=0; i<=t; i++) 51 sum[0][i]=1; 52 for(int i=1; i<=t; i++) 53 { 54 for(int j=1; j<=i; j++) 55 { 56 if(s[i-j+1]=='0') 57 continue; 58 dp[i][j]=(dp[i][j]+sum[i-j][j-1])%mod; 59 int l=i-j+1-j; 60 int r=i-j+1; 61 if(l<1) 62 continue; 63 if(lcp[l][r]<j&&s[l+lcp[l][r]]<s[r+lcp[l][r]]) 64 dp[i][j]=(dp[i][j]+dp[i-j][j])%mod; 65 } 66 for(int j=1; j<=t; j++) 67 sum[i][j]=(sum[i][j-1]+dp[i][j])%mod; 68 } 69 ll ans=0; 70 for(int i=1; i<=t; i++) 71 ans=(ans+dp[t][i])%mod; 72 printf("%I64d\n",ans); 73 //cout<<ans<<endl; 74 } 75 return 0; 76 }