• 压缩


    压缩( (dpstarstar))

    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)
    • 最后输出(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
    ",std::min(dp[1][n][1],dp[1][n][0]));
    }
    int main(){
        Init();
        Solve();
        return 0;
    }
    
  • 相关阅读:
    添加可运行的js代码
    一,IL访问静态属性和字段
    UI基础UIWindow、UIView
    ASP.NET MVC:会导致锁定的会话
    2013腾讯编程马拉松初赛
    使用phantomjs生成网站快照
    C语言
    设置 Ext.data.Store 传参的请求方式
    HDU 2041 超级楼梯
    MySQL 监控-innotop
  • 原文地址:https://www.cnblogs.com/hbhszxyb/p/13212805.html
Copyright © 2020-2023  润新知