BZOJ 1068: [SCOI2007]压缩
1068: [SCOI2007]压缩
Time Limit: 1 Sec Memory Limit: 128 MB
Submit: 1493 Solved: 941
[Submit][Status][Discuss]
Description
给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没有M,则从串的开始算起)开始的解压结果(称为缓冲串)。 bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程
另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。
Input
输入仅一行,包含待压缩字符串,仅包含小写字母,长度为n。
Output
输出仅一行,即压缩后字符串的最短长度。
Sample Input
bcdcdcdcdxcdcdcdcd
Sample Output
12
HINT
在第一个例子中,解为aaaRa,在第二个例子中,解为bMcdRRxMcdRR。
【限制】
100%的数据满足:1<=n<=50 100%的数据满足:1<=n<=50
题解
区间DP,f[i][j][0/1]表示i到j,内部是否添加M的最小压缩长度,认为i前面有一个M。
转移方程:
f[i][j][0]=min(f[i][k][0]+j-k)
f[i][j][1]=min(f[i][k][0/1]+1+f[k+1][j][0/1])
当区间长度为偶数并且区间前半部分和后半部分相当,那么可以讲后半部分变成R。
所以f[i][j][0]=min(f[i][j][0],f[i][mid][0]+1)。
最终答案为min(f[1][n][0],f[1][n][1])。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> using namespace std; const int N=55,inf=0x3f3f3f3f; int f[N][N][5]; char s[N]; int main(){ scanf("%s",s+1); int len=strlen(s+1); memset(f,inf,sizeof(f)); for(int i=1;i<=len;i++){ f[i][i][0]=1; } int l,r,mid,fg; for(int i=2;i<=len;i++){ for(int j=1;j+i-1<=len;j++){ l=j,r=j+i-1; for(int k=l;k<r;k++){ f[l][r][0]=min(f[l][r][0],f[l][k][0]+r-k); f[l][r][1]=min(f[l][r][1],min(f[l][k][0],f[l][k][1])+1+min(f[k+1][r][0],f[k+1][r][1])); } if(i%2==0){ fg=0; mid=i/2; for(int i=0;i<mid;i++){ if(s[l+i]!=s[l+mid+i]){ fg=1; break; } } if(!fg)f[l][r][0]=min(f[l][r][0],f[l][l+mid-1][0]+1); } } } printf("%d\n",min(f[1][len][0],f[1][len][1])); return 0; }