嘘~ 正在从服务器偷取页面 . . .

洛谷P2470 [SCOI2007]压缩 题解


洛谷P2470 [SCOI2007]压缩 题解

题目链接:P2470 [SCOI2007]压缩

题意

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

$\mathtt{bcdcdcdcd}$ 可以压缩为 $\mathtt{bMcdRR}$,下面是解压缩的过程:

已经解压的部分 解压结果 缓冲串
$\mathtt{b}$ $\mathtt{b}$ $\mathtt{b}$
$\mathtt{bM}$ $\mathtt{b}$ $\varnothing$
$\mathtt{bMc}$ $\mathtt{bc}$ $\mathtt{c}$
$\mathtt{bMcd}$ $\mathtt{bcd}$ $\mathtt{cd}$
$\mathtt{bMcdR}$ $\mathtt{bcdcd}$ $\mathtt{cdcd}$
$\mathtt{bMcdRR}$ $\mathtt{bcdcdcdcd}$ $\mathtt{cdcdcdcd}$

输入格式:

输入仅一行,包含待压缩字符串,仅包含小写字母,长度为 $n$ 。

输出格式:

输出仅一行,即压缩后字符串的最短长度。

数据范围

$100\%$ 的数据满足:$1\le n\le 50$

有史以来做过最神奇的区间dp

由于 $\tt{M}$ 的设计极其鬼畜,因此套路地设每个考虑的段均以 $\tt{M}$ 开头(可以无视这句话

首先设 $f_{i,j}$ 表示 $[i,j]$ 能被压缩到的最短长度(包括开头的 $\tt{M}$ )

则答案为 $f_{1,n} - 1$ ,且 $f_{i,i} = 2$

不过,在相同串拼接时的转移中,这个缓冲串检测的 $\tt{M}$ 只会向右移

例如我们想压缩 $ABBABB$ ($A,B$ 为某个子串)

那就不能提前压缩 $BB$ 为 $\mathtt{M}B\mathtt{R}$ ,这样后面更优的 $\mathtt{M}ABB\mathtt{R}$ 就无法使用了。

这一点应该很容易发现,但是怎么处理呢?

注意到此时的dp状态无法记录这样的情况

因为我们不知道当前处理的这一段中间有没有出现过 $\mathtt{M}$

嗨害嘿,这不都说出来了吗?

于是我们重新设 $f_{i,j}$ 表示 $[i,j]$ 能被压缩到的最短长度(包括开头的 $\tt{M}$ ),且除了开头的 $\mathtt{M}$ ,中间没有其他的 $\mathtt{M}$

相应地,设 $g_{i,j}$ 表示中间出现过别的 $\mathtt{M}$

考虑 $f_{i,j}$ 的转移。下文中用 $a\downarrow b+c$ 表示 $a\leftarrow \min\{a,b+c\}$。

第一种情况,两串合并,则要求 $k=\frac{i+j+1}{2} \in \mathbb{Z}$ ,且 $s[i\dots k-1]=s[k\dots j]$ ,则

第二种情况,直接添加字符

然后考虑 $g_{i,j}$ 的转移,没啥好考虑的,直接拼上

感觉做完了好像也没啥特别的

时间复杂度 $O(n^3)$

代码:

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdarg>
#include <cmath>
#include <iomanip>
#include <random>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
#define N ((int)(70))

void down(int &x,int y) { x > y ? x = y : 0;}

char s[N];
int n;
int f[N][N],g[N][N];

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    cin >> (s+1); n = strlen(s+1);
    memset(f,0x3f,sizeof(f)); memset(g,0x3f,sizeof(g));
    for(int i=1; i<=n; i++) f[i][i] = 2;
    for(int d=1,j,k,l; d<n; d++)
        for(int hd = d>>1, i=1; i<=n-d; i++)
        {
            if(j=i+d, d&1) // can split
            {
                for(l=j-hd, k=0; k<=hd; k++) if(s[i+k] != s[l+k]) break;
                if(k > hd) down(f[i][j], f[i][l-1]+1);
            }
            for(int k=i; k<j; k++)
            {
                down(f[i][j], f[i][k] + j - k);
                down(g[i][j], min(f[i][k], g[i][k]) + min(f[k+1][j], g[k+1][j]));
            }
        }
    cout << min(f[1][n], g[1][n]) - 1 << '\n';
    return 0;
}

参考文献

[1] https://yhx-12243.github.io/OI-transit/records/lydsy1068%3Blg2470.html


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录