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
思路:这道题和1090很像,一开始做的时候直接压缩某段加M,恩恩还是太天真了,这样子处理的话M可能会交叉,
可能或出现多个M。
那么怎么处理呢。
思路如下:
dp{i,j,k}表示第i到第j这一段的最小长度。k表示该段是否含有M。
我们要分情况处理,首先呢,我们分成两段进行压缩,且默认两端都hasM为1,在两端中间加个M,
a....an M b....bn 这样子压缩的话我们就不要担心前一段会影响后一段了。
第二种情况,就是我们只压缩前面一段,后面一段不用压缩。t=Math.min(t,dp(l,i,hasM)+(r-i));
第三种情况,就是我们判断整一段,择半处理,如果前一段和后一段一样,那么我们就把后一段替换为R,
继续处理前一段即可,这一段是不存在M的。因为这样最后一定会压缩成xxxRRR的形式,M的话我们已经在之前就处理过了。
import java.io.InputStreamReader;
import java.util.Scanner;
public class __1068 {
static boolean[][][] vis;
static int[][][] f;
static String s;
static boolean repeat(int l,int r){
int t=r-l+1;
if((t&1)==1){
return false;
}
for(int i=l;i<=(l+r)/2;++i){
if(s.charAt(i)!=s.charAt(i+t/2)){
return false;
}
}
return true;
}
public static int dp(int l,int r,int hasM){
if(l==r){
return 1;
}
if(vis[l][r][hasM]){
return f[l][r][hasM];
}
vis[l][r][hasM]=true;
int t=r-l+1;
if(hasM==1){
for(int i=l;i<r;++i){
t=Math.min(t,dp(l,i,1)+dp(i+1,r,1)+1);
}
}
for(int i=l;i<r;++i){
t=Math.min(t,dp(l,i,hasM)+(r-i));
}
if(repeat(l,r)){
t=Math.min(t,dp(l,(l+r)/2,0)+1);
}
return f[l][r][hasM]=t;
}
public static void main(String[] args){
Scanner cin=new Scanner(new InputStreamReader(System.in));
s=cin.next();
vis=new boolean[s.length()][s.length()][2];
f=new int[s.length()][s.length()][2];
System.out.println(dp(0,s.length()-1,1));
}
}