洛谷 P2470 [SCOI2007]压缩

给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没有M,则从串的开始算起)开始的解压结果(称为缓冲串)。

求一个仅含小写字母的字符串压缩后的最短长度

一道区间dp题,但是可能不太好想?

首先我们如果设(f_{l,r})表示区间l~r的最短长度,发现M的位置是会影响我们统计的

所以我们不妨设(f_{l,r,1/0})表示区间l~r有或没有M的最短长度,并且默认l-1的位置是有M的

这样就容易转移多了

[egin{cases} f_{l,r,0}=f_{l,mid,0}+1 (2|(r-l+1)& s_{ldots mid}=s_{mid+1dots r}) \ f_{l,r,0}=min_{k=l}^{r-1}(f_{l,k,0}+r-k) \ f_{l,r,1}=min_{k=l}^{r-1}(min(f_{l,k,0},f_{l,k,0})+1+min(f_{k+1,r,0},f_{k+1,r,1})) end{cases}]

第一种情况是左半边等于右半边,那么就可以被压缩,因为l-1有M,所以添个R就可以了

第二种情况是l~k这段被压缩过,因为这段区间没有M,所以后面不能被压缩,只能加上原长度

第三种情况因为这个区间插入了个M,所以两边有没有M无所谓了

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
const int N = 50;
using namespace std;
int n,f[N + 5][N + 5][2];
char ch[N + 5];
int check(int l,int r)
{
    int mid = l + r >> 1;
    for (int i = l;i <= mid;i++)
        if (ch[i] != ch[mid + i - l + 1])
            return 0;
    return 1;
}
int main()
{
    scanf("%s",ch + 1);
    n = strlen(ch + 1);
    for (int i = 1;i <= n;i++)
        for (int j = 1;j + i - 1 <= n;j++)
        {
            f[j][j + i - 1][0] = f[j][j + i - 1][1] = i;
            int mid = (j + j + i - 1) >> 1;
            if (i % 2 == 0 && check(j,j + i - 1))
                f[j][j + i - 1][0] = min(f[j][j + i - 1][0],f[j][mid][0] + 1);     
            for (int k = j;k < j + i - 1;k++)
            {
                f[j][j + i - 1][0] = min(f[j][j + i - 1][0],f[j][k][0] + j + i - 1 - k);
                f[j][j + i - 1][1] = min(f[j][j + i - 1][1],min(f[j][k][1],f[j][k][0]) + min(f[k + 1][j + i - 1][0],f[k + 1][j + i - 1][1]) + 1);
            }
        }
    cout<<min(f[1][n][1],f[1][n][0])<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/sdlang/p/13068277.html