这题比较巧妙的是运用最长公共前缀长度简化问题以及运用sum数组缩减复杂度。注意要求分割后所有数字不得出现前导零。
题解:点击打开链接
没用sum数组的超时代码:
#include<bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; int lcp[5001][5001]; ll dp[5001][5001]; int main(){ int n; cin>>n; string x; cin>>x; x='0'+x; for(int i=n;i>=1;--i){ for(int j=n;j>i;--j){ if(x[i]==x[j]) lcp[i][j]=lcp[i+1][j+1]+1; } } for(int i=1;i<=n;++i) dp[0][i]=1; for(int i=1;i<=n;++i){ //这个子串的结尾位置 for(int j=1;j<=i;++j){ //dp[i][j]:以【从x[i]往前j长度的子串】结尾的可能数 int p=i-j+1; //这个子串的开头位置 if(x[p]=='0') continue; for(int k=p-1;k>=max(p-j,0);--k){ //枚举上一个子串的开头位置(上一个一定不能比这个长) char a=x[k+lcp[k][p]],b=x[p+lcp[k][p]]; if(k!=p-j||(lcp[k][p]<j&&a<b)) dp[i][j]=(dp[i][j]+dp[p-1][p-k])%mod; } } } ll s=0; for(int i=1;i<=n;++i){ s=(s+dp[n][i])%mod; } cout<<s<<endl; return 0; }
AC代码:
#include<bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; int lcp[5001][5001]; ll dp[5001][5001]; ll sum[5001][5001]; int main(){ int n; cin>>n; string x; cin>>x; x='0'+x; for(int i=n;i>=1;--i){ for(int j=n;j>i;--j){ if(x[i]==x[j]) lcp[i][j]=lcp[i+1][j+1]+1; } } for(int i=0;i<=n;++i) sum[0][i]=1; for(int i=1;i<=n;++i){ //这个子串的结尾位置 for(int j=1;j<=i;++j){ //dp[i][j]:以【从x[i]往前j长度的子串】结尾的方法数 int p=i-j+1; //这个子串的开头位置 if(x[p]=='0') continue; // for(int k=p-1;k>=max(p-j,0);--k){ //枚举上一个子串的开头位置(上一个一定不能比这个长) // char a=x[k+lcp[k][p]],b=x[p+lcp[k][p]]; // if(k!=p-j||(lcp[k][p]<j&&a<b)) // dp[i][j]=(dp[i][j]+dp[p-1][p-k])%mod; // } dp[i][j]=sum[p-1][j-1]; //sum[i][j]表示符合当前【最后一个的长度小于j】的方法数(上一个比这个短当然较小) //考虑完上一个比这个短的情况,接下来只需要考虑长度相等的情况 int k=p-j; if(k<1) continue; char a=x[k+lcp[k][p]],b=x[p+lcp[k][p]]; if(lcp[k][p]<j&&a<b) dp[i][j]=(dp[i][j]+dp[p-1][j])%mod; } for(int j=1;j<=n;++j){ //这里注意是【<=n】而不是【<=i】,因为dp[i][j]=sum[p-1][j-1];会用到之前的状态(p-1),j-1可能大于p-1 sum[i][j]=(sum[i][j-1]+dp[i][j])%mod; } } ll s=0; for(int i=1;i<=n;++i){ s=(s+dp[n][i])%mod; } cout<<s<<endl; return 0; }
#include<bits/stdc++.h> #define ll long long #define mod 1000000007 using namespace std; int lcp[5001][5001]; ll dp[5001][5001]; int main(){ int n; cin>>n; string x; cin>>x; x='0'+x; for(int i=n;i>=1;--i){ for(int j=n;j>i;--j){ if(x[i]==x[j]) lcp[i][j]=lcp[i+1][j+1]+1; } } for(int i=1;i<=n;++i) dp[0][i]=1; for(int i=1;i<=n;++i){ //这个子串的结尾位置 int w=i; for(int j=1;j<=i;++j){ //dp[i][j]:以【从x[i]往前j长度的子串】结尾的方法数 int p=i-j+1; //这个子串的开头位置 if(x[p]!='0') w=p; //这个子串的【非0】开头位置 int h=p-1; for(int k=p-1;k>=0;--k){ //枚举上一个子串的开头位置(上一个一定不能比这个长,错!比如0012,001比2长但比2小) if(x[k]!='0') h=k; //这个子串的【非0】开头位置 char a=x[h+lcp[h][w]],b=x[w+lcp[h][w]]; if(p-h<i-w+1||(p-h==i-w+1&&lcp[h][w]<j&&a<b)) //【这个串较长】或【一样长比较公共序列后第一个不同字符】 dp[i][j]=(dp[i][j]+dp[p-1][p-k])%mod; } } } ll s=0; for(int i=1;i<=n;++i){ s=(s+dp[n][i])%mod; } cout<<s<<endl; return 0; }