BZOJ 1068: [SCOI2007]压缩 区间dp

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));
    }

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值