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\n",min(f[1][len][0],f[1][len][1]));

    return 0;
}

 

posted @ 2018-02-20 11:26  zbtrs  阅读(...)  评论(...编辑  收藏