压缩
压缩( \(dp\star\star\))
Descrption
-
给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小写字母外还可以(但不是必需)包含大写字母 \(R\) 与 \(M\),其中\(M\)标记重复串的开始,\(R\)重复从上一个\(M\)(如果当前位置左边没有\(M\),则从串的开始算起)开始的解压结果(称为缓冲串)。
-
\(bcdcdcdcd\) 可以压缩为 \(bMcdRR\),下面是解压缩的过程:
已经解压的部分 解压结果 缓冲串 $ b $ $ ab $ \(b\) \(bM\) $ bb $ \(bMc\) \(bc\) \(c\) \(bMcd\) \(bcd\) \(cd\) \(bMcdR\) \(bcdcd\) \(cdcd\) \(bMcdRR\) \(bcdcdcdcd\) \(cdcdcdcd\) - 另一个例子是 \(abcabcdabcabcdxyxyz\) 可以被压缩为 \(abcRdRMxyRz\)。
Input
- 输入仅一行,包含待压缩字符串,仅包含小写字母,长度为\(n\)。
Output
- 输出仅一行,即压缩后字符串的最短长度。
Sample Input1
aaaaaaa
Sample Output1
5
Sample Input2
bcdcdcdcdxcdcdcdcd
Sample Output2
12
Hint
- 样例说明:在第一个例子中,解为\(aaaRa\),在第二个例子中,解为\(bMcdRRxMcdRR\)。
- 数据范围:
- \(50\%\) 的数据满足:\(1<=n<=20\)。
- \(100\%\) 的数据满足:\(1<=n<=50\)。
分析
- 设 \(dp[i][j][0]\) 表示表示 \(i\) 到 \(j\) 的区间内没有 \(M\) 的情况:
- 如果前半段与后半段相等,\(dp[i][j][0]=min(dp[i][j][0],dp[l][mid][0]+1),(mid=(i+j)/2)\)
- 如果\([i,j]\),但有可能 \([i,k],(i<k<j)\) 可以折叠,\(dp[i][j][0]=min(dp[i][j][0],dp[i][k][0]+j-k)\)
- \(dp[i][j][1]\) 表示 \(i\) 到 \(j\) 的区间内有 \(M\) 的情况:
- \(k\) 枚举的是 \(M\) 的位置,即在 \(k\) 的后面放一个 \(M\):
- \(dp[l][r][1]=min(dp[l][r][1],min(dp[l][k][0],dp[l][k][1])+min(dp[k+1][r][0],dp[k+1][r][1])+1)\)
- 对区间 \([l,k]\) 和 \([k+1,r]\) 均有两种选择,然后加上 \(M\) 这个 \(1\) 。
- \(k\) 枚举的是 \(M\) 的位置,即在 \(k\) 的后面放一个 \(M\):
- 最后输出\(ans=max(dp[1][n][0],dp[1][n][1])\)。
- 对 \(dp[i][j][1]\) 是两个递归的子问题合并而成,所以套用典型的区间 \(dp\) 的模板,注意对 \(M\) 的处理。
Code
#include <bits/stdc++.h>
const int maxn=50+5;
char a[maxn];
int dp[maxn][maxn][3];
int n;
int check(int l,int r){//判断前一半是否和后一半相等
if((r-l+1)&1)return 0;//长度为奇数
int mid=(l+r)>>1;
for(int i=l;i<=mid;i++)
if(a[i]!=a[i+mid-l+1])return 0;
return 1;
}
void Init(){
scanf("%s",a+1);
n=strlen(a+1);
memset(dp,0x3f,sizeof(dp));
}
void Solve(){
for(int i=1;i<=n;i++)//初始化为没有折叠
for(int j=i;j<=n;j++)
dp[i][j][0]=dp[i][j][1]=(j-i+1);
for(int d=2;d<=n;d++){//区间dp长度为2开始
for(int i=1,j;(j=i+d-1)<=n;i++){//i为区间起点,j为区间终点
if(check(i,j))//区间[i,j]正好能折叠
dp[i][j][0]=std::min(dp[i][(i+j)/2][0]+1,dp[i][j][0]);
for(int k=i;k<j;k++)//枚举区间的断点,有可能[i,k]能折叠
dp[i][j][0]=std::min(dp[i][j][0],dp[i][k][0]+j-k);
for(int k=i;k<j;k++)//枚举M的位置,即在k的后面加一个M
dp[i][j][1]=std::min(dp[i][j][1],std::min(dp[i][k][0],dp[i][k][1])+std::min(dp[k+1][j][0],dp[k+1][j][1])+1);
}
}
printf("%d\n",std::min(dp[1][n][1],dp[1][n][0]));
}
int main(){
Init();
Solve();
return 0;
}
hzoi