第一步先求出最后的那个数最小为多少。(为了叙述方便,记T(i,j)表示从原数列下标i取到j的数字组成的数。)只需正向dp一次,dp1[i]表示前i个数字分成任意多个递增数且最后的数最小时,最后的数为T(dp1[i],i)。则 d p 1 [ i ] = m a x ( j ) , ( T ( d p 1 [ j − 1 ] , j − 1 ) < T ( j , i ) ) dp1[i]=max(j),(T(dp1[j-1],j-1)<T(j,i)) dp1[i]=max(j),(T(dp1[j−1],j−1)<T(j,i))。
第二步要求最后一个数确定的情况下,前面的数字按字典序尽量大的解。类似上面的方法反向动归一次即可。
算法复杂度 O ( l 3 ) O(l^3) O(l3)。由于数据大部分为随机,实际运行效率接近 O ( l 2 ) O(l^2) O(l2)。
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 500 + 5;
int n;
char s[N];
int a[N],d[N],f[N];
inline int cmp(int l1,int r1,int l2,int r2) {
while(l1<=r1&&a[l1]==0) l1++;
while(l2<=r2&&a[l2]==0) l2++;
if(r1-l1+1==0) return r2-l2+1==0 ? 0 : 1;
if(r1-l1+1 < r2-l2+1) return -1;
if(r1-l1+1 > r2-l2+1) return 1;
int len = r1-l1+1;
for(int i=0;i<len;i++) {
if(a[l1+i] < a[l2+i]) return -1;
if(a[l1+i] > a[l2+i]) return 1;
}
return 0;
}
inline void dp1() {
for(int i=1;i<=n;i++) {
d[i] = 1;
for(int j=i;j>=1;j--) {
if(cmp(d[j-1],j-1,j,i) == -1) {d[i]=j;break;}
}
}
}
inline void dp2() {
f[d[n]] = n; int lastzero = d[n];
while(a[lastzero-1]==0) f[lastzero-1] = n, lastzero--;
for(int i=d[n]-1;i>=1;i--) {
for(int j=d[n]-1;j>=i;j--) {
if(cmp(i,j,j+1,f[j+1]) == -1){f[i]=j;break;}
}
}
}
inline void print() {
int pos = 1,flag = 0;
while(pos <= n) {
if(flag) putchar(',');
flag = 1;
for(int i=pos;i<=f[pos];i++) printf("%d",a[i]);
pos = f[pos] + 1;
}
}
int main() {
scanf("%s",s+1); n = strlen(s+1);
for(int i=1;i<=n;i++) a[i] = s[i] - '0';
dp1();
dp2();
print();
return 0;
}