bzoj1090
区间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
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;i1,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));
}