题解
区间DP&记忆化搜索。
设
dp[l][r]
d
p
[
l
]
[
r
]
表示字符串
[l,r]
[
l
,
r
]
区间的最小表示(
l=r时,dp[l][r]=1
l
=
r
时
,
d
p
[
l
]
[
r
]
=
1
)。
枚举分割点,先不考虑两段之间的折叠,得到:
dp[l][r]=min(dp[l][k]+dp[k+1][r])(l≤k<r)
d
p
[
l
]
[
r
]
=
m
i
n
(
d
p
[
l
]
[
k
]
+
d
p
[
k
+
1
]
[
r
]
)
(
l
≤
k
<
r
)
再考虑两段之间的折叠关系,直接判断
[l,k]
[
l
,
k
]
是否可以由
[k+1,r]
[
k
+
1
,
r
]
折叠得到,再计算长度取
min
m
i
n
。
代码
#include<bits/stdc++.h>
using namespace std;
const int hs=79,md=998244353;
int n,rg[110],pw[110],f[110][110];
char s[110];
inline int ad(int x,int y){x+=y;if(x>=md) x-=md;return x;}
inline int mul(int x,int y){return 1ll*x*y%md;}
inline int dc(int x,int y){x-=y;if(x<0) x+=md;return x;}
inline int hash(int l,int r){return dc(rg[r],mul(rg[l-1],pw[r-l+1]));}
inline bool check(int l,int mid,int r)
{
int i,bs=mid-l+1,tp=hash(l,mid);
for(i=mid+1;i<=r;i+=bs)
if(hash(i,i+bs-1)!=tp) return false;
return true;
}
inline int cal(int x)
{
int re=1;
for(;x>9;x/=10) re++;
return re;
}
inline int dp(int l,int r)
{
if(f[l][r]!=0) return f[l][r];
if(l+4>r) return (f[l][r]=r-l+1);
int x,y,i,re=110,len=r-l+1;
for(int i=l;i<r;++i){
x=dp(l,i);y=dp(i+1,r);
re=min(re,x+y);
if(len%(i-l+1)!=0 || r+l<2*i+1 || (r-i)%(i-l+1)!=0 || !check(l,i,r)) continue;
re=min(re,x+2+cal(len/(i-l+1)));
}
return (f[l][r]=re);
}
int main(){
int i,j;
scanf("%s",s+1);
n=strlen(s+1);
rg[0]=0;pw[0]=1;
rg[1]=s[1];
for(i=2;i<=n;++i) rg[i]=ad(mul(rg[i-1],hs),s[i]);
for(i=1;i<=n;++i) pw[i]=mul(pw[i-1],hs);
printf("%d\n",dp(1,n));
}