题意:
给一个数字串(长度n<5000),问有几种分割数字串的方式,使得在原来的顺序上,后一个数字串表示的数字肯定比前一个大。
dp[b][c] "b~c"为最后一个数字时的方法数
前一个数位数比最后一个数位数小时 dp[b][c]=sigma(dp[a][b-1]]) b-1-a<c-b a=2b-c~b-1
若a=2b-c-1 则比较数"a~b-1" 与"b~c"大小
nxt[a][b]=x 为第一个x满足s[a+x]!=s[b+x] ,nxt[a][b]=s[a]==s[b]?nxt[a+1][b+1]+1:0;
dp[i][j+1]=dp[i][j](Added后)+smaller(true) 复杂度从O(n^3)->O(n^2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=5e3+20;
char s[N];
int n;
int dp[N][N],nxt[N][N];
void init()
{
memset(nxt,0,sizeof(nxt));
for(int i=n;i>=1;i--)
{
for(int j=n;j>=i;j--)
{
if(s[i]!=s[j])
nxt[i][j]=0;
else
nxt[i][j]=nxt[i+1][j+1]+1;
}
}
}
bool smaller(int i,int j,int len)
{
if(nxt[i][j]>=len) return false;
else return s[i+nxt[i][j]]<s[j+nxt[i][j]];
}
int main()
{
while(cin>>n)
{
memset(dp,0,sizeof(dp));
scanf("%s",s+1);
init();
for(int j=1;j<=n;j++)
dp[1][j]=1;
for(int i=2;i<=n;i++)
{
if(s[i]=='0')
continue;
int sum=0;
//同一个i,递推式为后缀和形式 可以优化
for(int j=i;j<=n;j++)
{
int len=j-i+1;
bool add=false;//相同长度是否加
if(i-len>=1&&smaller(i-len,i,len))
{
(sum+=dp[i-len][i-1])%=mod;
add=true;
}
dp[i][j]=sum;
if(i-len>=1&&(!add))
(sum+=dp[i-len][i-1])%=mod;
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
ans=(ans+dp[i][n])%mod;
}
cout<<ans<<endl;
}
return 0;
}