BZOJ1068 [SCOI2007]压缩 区间动态规划 字符串

欢迎访问~原文出处——博客园-zhouzhendong

去博客园看该题解


题目传送门 - BZOJ1068


 

题目概括

  (其实是复制的)

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

 

  另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。


 

题解

  这又是一道字符串的题目。

  记得有人说过——字符串的题目就是哈希+乱搞????

  言归正传:

  一看就是一道区间动归。

  所以我们用:

    g[i][j]表示禁止更新缓冲,但是允许解压R的情况 (最短值)
    f[i][j]表示允许更新缓冲,但是禁止解压R的情况 (最短值)

  于是只需要写两个记忆化dfs就可以了。

  具体操作看代码。


代码

#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;
const int N=50+5;
char str[N];
int n,g[N][N],f[N][N];
// g[i][j]表示禁止更新缓冲,但是允许解压R的情况 
// f[i][j]表示允许更新缓冲,但是禁止解压R的情况 
bool can_fold[N][N];
bool fold_check(int L,int R){
    int len=R-L+1;
    if (len&1)
        return 0;
    int M=L+len/2;
    for (int i=0;i<len/2;i++)
        if (str[L+i]!=str[M+i])
            return 0;
    return 1;
}
int G(int L,int R){
    if (L>R)
        return 0;
    if (g[L][R]!=-1)
        return g[L][R];
    g[L][R]=R-L+1;
    for (int i=1;L+i*2-1<=R;i++){
        int M=L+i*2-1;
        if (can_fold[L][M])
            g[L][R]=min(g[L][R],G(L,L+i-1)+1+(R-M));
    }
    return g[L][R];
}
int F(int L,int R){
    if (L>R)
        return 0;
    if (f[L][R]!=-1)
        return f[L][R];
    f[L][R]=G(L,R);
    for (int i=L;i<=R;i++)
        f[L][R]=min(f[L][R],min(G(L,i)+1+F(i+1,R),G(L,i)+(R-i)));
    return f[L][R];
}
int main(){
    scanf("%s",str+1);
    n=strlen(str+1);
    memset(can_fold,0,sizeof can_fold);
    for (int i=1;i<=n;i++)
        for (int j=i;j<=n;j++)
            can_fold[i][j]=fold_check(i,j);
    memset(f,-1,sizeof f);
    memset(g,-1,sizeof g);
    for (int i=1;i<=n;i++)
        f[i][i]=g[i][i]=1;
    printf("%d",F(1,n));
    return 0;
} 

 

posted @ 2017-08-15 21:39  zzd233  阅读(391)  评论(0编辑  收藏  举报