• bzoj1068 [SCOI2007]压缩


    1068: [SCOI2007]压缩

    Time Limit: 1 Sec  Memory Limit: 128 MB
    Submit: 1574  Solved: 1004
    [Submit][Status][Discuss]

    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

    分析:挺巧妙的一道题.

       首先可以一眼看出来这是一个区间dp,但是直接想状态以及状态转移方程很难.一般的区间dp状态都表示为f[l][r]表示将区间[l,r]处理后的最小长度,但在这题里似乎还缺点什么.

       考虑一下搜索.首先需要知道,在串的左侧默认有一个“M”,这是不计入串长的.每次搜区间[l,r]能被压缩成多短.接下来你有几种操作:1.找个位置放R. 2.找个位置放M.  

       对于第一个操作,如果R放在串的中间正好,那么这个串的长度就缩成了一半,可以递归处理下去. 如果R放在偏右位置,没有意义.如果R放在偏左位置,那么可以将串一分为二,左边放了R的部分继续递归,右边就用原串,结束递归. question:为什么在这里不讨论右边放R呢?如果右边放R,那么前面必有一个M,这就属于第二个操作了.

       对于第二个操作,找了一个位置放M后,可以发现左右两半就是一模一样的子问题了,递归下去.

       可以发现,第二个操作的M对于第一个操作的R是一个制约左右,只有左边放了M才能放R.

       到这里,记忆化搜索一下就可以了.当然,也可以用递推.令f[i][j][0/1]表示区间[l,r]中有没有放M压缩后最短的串. 具体的递推方程看我的代码吧:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    const int inf = 0x7ffffff;
    
    char s[60];
    int len,f[60][60][2];
    
    bool check(int x,int y)
    {
        if ((y - x + 1) & 1)
            return false;
        int mid = (x + y) >> 1;
        for (int i = 1; i <= mid - x + 1; i++)
        {
            if (s[i + x - 1] != s[mid + i])
                return false;
        }
        return true;
    }
    
    int main()
    {
        scanf("%s",s + 1);
        len = strlen(s + 1);
        for (int l = 1; l <= len; l++)
        {
            for (int i = 1; i + l - 1 <= len; i++)
            {
                int j = i + l - 1;
                f[i][j][0] = f[i][j][1] = (j - i + 1);
                for (int k = i; k < j; k++)
                    f[i][j][1] = min(f[i][j][1],min(f[i][k][0],f[i][k][1]) + 1 + min(f[k + 1][j][0],f[k + 1][j][1]));
                for (int k = i; k < j; k++)
                    f[i][j][0] = min(f[i][j][0],f[i][k][0] + j - k);
                if (check(i,j))
                    f[i][j][0] = min(f[i][j][0],f[i][(i + j) >> 1][0] + 1);
            }
        }
        printf("%d
    ",min(f[1][len][0],f[1][len][1]));
    
        return 0;
    }
  • 相关阅读:
    Leetcode 217 存在重复
    Leetcode 125验证回文串
    HTML标签
    Having dreams is what makes life tolerable.
    Database数据库——MySQL简述
    Python实践之路8——选课系统
    Python学习之路16——线程、进程和协程
    Python实践之路7——计算器
    Python学习之路14——Socket
    Python学习之路13——异常处理
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8454935.html
Copyright © 2020-2023  润新知