题目大意:给定一个字符串,求按照题中所给的压缩方式最短能压缩到多长
区间DP 令f[i][j]表示[i,j]区间内的字符串最短能压缩到多长
普通的区间DP:f[i][j]=min{f[i][k]+f[k+1][j]} (i<=k<=j-1)
此外如果对这段字符串进行压缩,那么我们可以枚举循环节,用Hash来判断
如果k是一个循环节,那么有f[i][j]=min(f[i][j],f[i][i+k-1]+digit[len/k]+2)
其中len=j-i+1,digit表示一个数在十进制下的长度
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 110
#define BASE 151
using namespace std;
typedef unsigned long long ll;
int n,digit[M],f[M][M];
char s[M];
ll hash[M],power[M];
void Pretreatment()
{
int i;
for(i=1;i<=n;i++)
digit[i]=digit[i/10]+1;
for(power[0]=1,i=1;i<=n;i++)
power[i]=power[i-1]*BASE;
for(i=1;i<=n;i++)
hash[i]=hash[i-1]*BASE+s[i];
}
int main()
{
int i,j,k,len;
scanf("%s",s+1);n=strlen(s+1);
Pretreatment();
memset(f,0x3f,sizeof f);
for(i=1;i<=n;i++)
f[i][i]=1;
for(len=2;len<=n;len++)
for(i=1;(j=i+len-1)<=n;i++)
{
for(k=i;k<j;k++)
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
for(k=1;k<n;k++)
if(len%k==0)
{
ll hash1=hash[j-k]-hash[i-1]*power[len-k];
ll hash2=hash[j]-hash[i+k-1]*power[len-k];
if(hash1!=hash2)
continue;
f[i][j]=min(f[i][j],f[i][i+k-1]+digit[len/k]+2);
}
}
cout<<f[1][n]<<endl;
return 0;
}