f[i = 以i结尾][j = 长度为j] = 方案数。
f[i][j] = sum{ f[i-j][k] , k < j || (k == j && s(i-j+1,j) > s(i-2*j+1,j) ) }
转移为O(N^3)需要优化,
对于k < j,递推g[i][j] = sum(f[i][k], k <= j)。
对于k == j,有O(N^2)个后缀,可以用二维数组lcp[i][j]递推i和j开头的最长公共前缀。
(后缀数组倍增大概也可以做的,用memcpy都可以过的,常数还比较小,极限数据1481ms。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e3+5, mod = 1e9+7; int f[N][N], g[N][N]; char s[N]; int lcp[N][N]; bool bigger(int i, int j, int len) { if(lcp[i][j] >= len) return false; else { int c = lcp[i][j]; return s[i+c] > s[j+c]; } } //#define LOCAL int main() { #ifdef LOCAL freopen("in.txt","r",stdin); #endif int n, i, j; scanf("%d%s", &n, s+1); if(s[1] == '0'){ puts("0"); return 0; } for(i = n; i > 0; i--){ for(j = n; j > 0; j--){ if(s[i] == s[j]) lcp[i][j] = lcp[i+1][j+1]+1; } } for(i = 1; i <= n; i++){ for(j = 1; j < i; j++) { g[i][j] = g[i][j-1]; if(s[i-j+1] != '0'){ f[i][j] = g[i-j][min(j-1,i-j)]; if(f[i-j][j] && bigger(i-j+1,i-j-j+1,j)){ f[i][j] += f[i-j][j]; if(f[i][j] >= mod) f[i][j] -= mod; } g[i][j] += f[i][j]; if(g[i][j] >= mod) g[i][j] -= mod; } } f[i][i] = 1; g[i][i] = g[i][i-1] + 1; if(g[i][i] == mod) g[i][i] = 0; } printf("%d\n",g[n][n]); return 0; }