【ZJ选讲·压缩】
给一个由小写字母组成的字符串(len<=50)
我们可以用一种简单的方法来压缩其中的重复信息。
用M,R两个大写字母表示压缩信息 M标记重复串的开始,
R表示后面的一段字符串重复从上一个M到R之前的那一段。
(一开始字符串最前面有一个不用写出来的M)
来点神奇例子: abcabcdabcabcdxyxyz abcRdRMxyRz 问压缩后的最短长度
【题解】
①区间DP。
②f[i][j][0/1]表示区间[i,j]中间是否填'M'
状态转移的特点是拆分区间左边没M右边有M然后右边继续左右操作
1).dp[l][r][0]=min{dp[l][r][0],dp[l][i][0]+r-i} 表示i~r不压缩
2).如果区间长度(r-l+1)%2==0且前后两段字符相同(s[l~mid]==s[mid+1~r])
dp[l][r][0]=min(dp[l][r][0],dp[l][mid][0]+1) 表示放一个R在mid和mid+1之间
3).dp[l][r][1]=min{dp[l][r][1],min(dp[l][i][0],dp[l][i][1])+1+min(dp[i+1][r][0],dp[i+1][r][1])} 表示在i,i+1之间放一个M,那么l~i和i+1~r就是两个独立的区间了。
#include<cstdio> #include<cstring> #include<iostream> using namespace std; char s[55]; bool vis[55][55]; int dp[55][55][2],n; bool check(int l,int m){ for(int i=0;l+i<m;i++) if(s[l+i]!=s[m+i]) return 0; return 1; } void dfs(int l,int r){ if(vis[l][r]) return; vis[l][r]=1; for(int i=l;i<r;i++) dfs(l,i),dfs(i+1,r); int &ret0=dp[l][r][0],&ret1=dp[l][r][1]; ret0=ret1=r-l+1; //1 for(int i=l;i<r;i++) ret0=min(ret0,dp[l][i][0]+r-i); //2 if((r-l+1)%2==0){ int mid=(l+r)>>1; if(check(l,mid+1)) ret0=min(ret0,dp[l][mid][0]+1); } //3 for(int i=l;i<r;i++) ret1=min(ret1,min(dp[l][i][0],dp[l][i][1])+1+min(dp[i+1][r][0],dp[i+1][r][1])); } int main(){ scanf("%s",s+1); n=strlen(s+1); dfs(1,n); printf("%d",min(dp[1][n][0],dp[1][n][1])); return 0; }//*ZJ
.