题意: 给定一个字符串, 求其最短压缩长度
折叠的定义如下:压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没有M,则从串的开始算起)开始的解压结果(称为缓冲串)。
例如:bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程:
已解压的部分 | 解压结果 | 缓冲串 |
---|---|---|
b | b | b |
bm | b | . |
bmc | bc | c |
bmcd | bcd | cd |
bmcdr | bcdcd | cdcd |
bmcdrr | bcdcdcdcd | cdcdcdcd |
>> face <<
Strategy:这题和上一题类似, l略有不同, 如果按照上一题的思路:dp[l][r]代表已经处理好了的最优压缩区间, 很容易想到二分字串是否相等, 但是相等之后如何处理要加的M呢, 也就是说, 这里的m是有后效性的, m的位置对以后的递推是有影响的, 为了规避这个问题, 我们可以再开一维
状态: { d p [ l ] [ r ] [ 0 ] → 在 [ l , r ] 区 间 内 没 有 m 的 最 优 压 缩 长 度 d p [ l ] [ r ] [ 1 ] → 在 [ l , r ] 区 间 内 含 有 m 的 最 优 压 缩 长 度 \begin{cases}dp[l][r][0]\to 在[l, r]区间内没有m 的最优压缩长度\\dp[l][r][1]\to 在[l, r]区间内含有m的最优压缩长度 \end{cases} {dp[l][r][0]→在[l,r]区间内没有m的最优压缩长度dp[l][r][1]→在[l,r]区间内含有m的最优压缩长度
目标: min ( d p [ 1 ] [ n ] [ 0 ] , d p [ 1 ] [ n ] [ 1 ] ) \min(dp[1][n][0], dp[1][n][1]) min(dp[1][n][0],dp[1][n][1])
边界: d p [ i ] [ i ] [ 0 ] = d p [ i ] [ i ] [ 1 ] = 1 ( i ∈ [ 1 , s t r i n g . s i z e ( ) ] ) , d p [ l ] [ r ] = r − l + 1 dp[i][i][0] = dp[i][i][1] = 1(i\in [1, string.size()]), dp[l][r] = r - l + 1 dp[i][i][0]=dp[i][i][1]=1(i∈[1,string.size()]),dp[l][r]=r−l+1
合法判断: 条件转移的设计
转移策略: 对于dp[l][r][0],先处理其前缀压缩,该字串能被压缩, 显然就是 d p [ l ] [ m i d ] [ 0 ] + 1 dp[l][mid][0] + 1 dp[l][mid][0]+1其中加一代表R, 对于dp[l][r][1], 我们必须要枚举m的位置
转移方程: 以长度为阶段划分, 枚举区间断点, 枚举未知, 让所有能转移到该未知的状态来转移
attention: 转移, 区间
双倍经验: 经典, 重点是规避m的后效性
/*
* I Love Coding!
*
* .::::.
* .::::::::.
* :::::::::::
* ..:::::::::::'
* '::::::::::::'
* .::::::::::
* '::::::::::::::..
* ..::::::::::::.
* ``::::::::::::::::
* ::::``:::::::::' .:::.
* ::::' ':::::' .::::::::.
* .::::' :::: .:::::::'::::.
* .:::' ::::: .:::::::::' ':::::.
* .::' :::::.:::::::::' ':::::.
* .::' ::::::::::::::' ``::::.
* ...::: ::::::::::::' ``::.
* ````':. ':::::::::' ::::..
* '.:::::' ':'````..
*/
#include <bits/stdc++.h>
#include <bits/extc++.h>
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define _rev(i, a, b) for (int i = (a); i >= (b); --i)
#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rof(i, a, b) for (int i = (a); i > (b); --i)
#define ll long long
#define db double
#define oo 0x3f3f3f3f
#define eps 0.00001
#define all(x) x.begin(), x.end()
#define met(a, b) memset(a, b, sizeof(a))
#define id(x) ((x + 8))
#define what_is(x) cerr << #x << " is " << x << endl
#define lowbit(x) x &(-x)
const int maxn = 59;
const int mod = 19650827;
using namespace std;
int dp[maxn][maxn][2], n;
string str;
int main()
{
ios::sync_with_stdio(0);
cin >> str;
n = str.size();
_rep(i, 1, n)
{
dp[i][i][1] = dp[i][i][0] = 1;
}
_rep(len, 2, n)
{
_rep(l, 1, n - len + 1)
{
int r = l + len - 1;
dp[l][r][1] = dp[l][r][0] = len;
_rep(k, l, r)//处理前缀压缩
dp[l][r][0] = min(dp[l][k][0] + r - k, dp[l][r][0]);
int mid = l + r >> 1;
if((len & 1) == 0 && str.substr(l-1, len>>1) == str.substr(mid, len>>1))
dp[l][r][0] = dp[l][mid][0] + 1;
_rep(k, l, r)//枚举m的位置
dp[l][r][1] = min(dp[l][r][1], min(dp[l][k][0], dp[l][k][1]) + 1 + min(dp[k+1][r][0], dp[k+1][r][1]));
}
}
cout << min(dp[1][n][1], dp[1][n][0]) << endl;
}