/*
用num(i,j)表示s[i~j]所表示的数
容易想到dp:
f[i]表示s[1~i],最后一段的开头的最大值,f[i]=max(j) (1<=j<=i,num(f[j-1],j-1)<num(j,i))
g[i]表示s[i~n],第一段的结尾的最大值,g[i]=max(j) (i<=j<=n,num(i,j)<num(j+1,g[j+1]))
直接暴力dp,时间复杂度O(n^3)
先考虑将字符串比较优化到O(logn):
若两个串长度相等,利用字符串哈希二分他们的最长公共前缀,再比较下一位即可
由于有前导0,维护L[i]和R[i]表示i左侧和右侧(包括他自己)第一个不等于0的数,方便比较
再考虑优化状态转移:
可以发现,f[i]能对f[j]有贡献当且仅当i<j且num(f[i],i)<num(i+1,j)
因此,合法的j是一个后缀
维护区间最大值线段树,支持区间变为与x的最大值和单点查询,可用标记持久化优化常数
g[i]也类似
*/
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N=1e6+5;
const ULL base=13331;
int n;
char s[N];
ULL h[N],p[N];
int L[N],R[N],f[N],g[N];
struct Segment_tree{
int l,r;
int tag;
}tr[N*4];
void init(){
p[0]=1;
for(int i=1; i<=n; i++){
h[i]=h[i-1]*base+s[i];
p[i]=p[i-1]*base;
}
L[0]=0,R[n+1]=n+1;
for(int i=1; i<=n; i++) L[i]=s[i]=='0'? L[i-1]:i;
for(int i=n; i>=1; i--) R[i]=s[i]=='0'? R[i+1]:i;
}
ULL Hash(int l,int r){
return h[r]-h[l-1]*p[r-l+1];
}
bool cmp(int l1,int r1,int l2,int r2){ //s[l1~r1]<s[l2~r2]
if(!r1) return 1;
int l=0,r=r1-l1+1;
while(l<r){
int mid=l+r+1>>1;
if(Hash(l1,l1+mid-1)==Hash(l2,l2+mid-1)) l=mid;
else r=mid-1;
}
if(l==r1-l1+1) return 0;
else return s[l1+l]<s[l2+l];
}
void build(int u,int l,int r){
tr[u]={l,r,0};
if(l==r) return;
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
}
void modify(int u,int l,int r,int x){
if(l<=tr[u].l&&tr[u].r<=r){
tr[u].tag=max(tr[u].tag,x);
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,x);
if(r>mid) modify(u<<1|1,l,r,x);
}
int query(int u,int p,int tag){
tag=max(tag,tr[u].tag);
if(tr[u].l==tr[u].r) return tag;
int mid=tr[u].l+tr[u].r>>1;
if(p<=mid) return query(u<<1,p,tag);
else return query(u<<1|1,p,tag);
}
int main(){
cin.tie(0)->sync_with_stdio(0);
while(cin>>s+1){
n=strlen(s+1);
init();
for(int i=0; i<=n+1; i++) f[i]=0;
f[0]=1;
build(1,1,n);
for(int i=1; i<=n; i++){
f[i]=query(1,i,f[i-1]);
int l1=R[f[i]],r1=i,l2=R[i+1],r2=l2+r1-l1; //保证长度相等
if(r2>n) continue;
if(!cmp(l1,r1,l2,r2)) r2++; //r2为第一个满足num(f[i],i)<num(i+1,r2)的r2
modify(1,r2,n,i+1);
}
for(int i=0; i<=n+1; i++) g[i]=0;
for(int i=L[f[n]-1]+1; i<=n; i++) g[i]=n;
build(1,1,n);
for(int i=f[n]; i>=1; i--){
g[i]=query(1,i,g[i]);
int l2=R[i],r2=g[i],r1=i-1,l1=r1-r2+l2;
if(l1>=1&&!cmp(l1,r1,l2,r2)) l1++;
l1=max(l1,1),l1=L[l1-1]+1;
modify(1,l1,i-1,i-1);
}
for(int i=1; i<=n; i=g[i]+1){
for(int j=i; j<=g[i]; j++) cout<<s[j];
if(g[i]+1<=n) cout<<',';
}
cout<<'\n';
}
return 0;
}