题意: 给定一个字符串, 求其最短折叠长度
折叠的定义如下:一个字符串可以看成它自身的折叠。记作S = S
X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) = SSSS…S(X个S)。
如果A = A’, B = B’,则AB = A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B) = AAACBB,而2(3(A)C)2(B) = AAACAAACBB
给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。
>> face <<
Strategy:区间dp
状态: d p [ l ] [ r ] → dp[l][r]\to dp[l][r]→该区间内折叠串的最短长度
目标: d p [ 1 ] [ n ] dp[1][n] dp[1][n]
边界: d p [ i ] [ i ] = 1 ( i ∈ [ 1 , s t r i n g . s i z e ( ) ] ) , d p [ l ] [ r ] = r − l + 1 dp[i][i] = 1(i\in [1, string.size()]), dp[l][r] = r - l + 1 dp[i][i]=1(i∈[1,string.size()]),dp[l][r]=r−l+1
合法判断: 这题的重难点就在判断一个串能否被折叠
转移策略: 如果该字串能被折叠, 那么转移方式就是被折叠串的长度加上括号的长度加上数字的长度
转移方程: 以长度为阶段划分, 枚举区间断点, 枚举未知, 让所有能转移到该未知的状态来转移
{ d p [ l ] [ r ] = m i n ( d p [ l ] [ r ] , d p [ l ] [ k ] + 2 + n u m ( 整 串 长 / 折 叠 因 子 长 ) ) , 如 果 能 被 折 叠 d p [ l ] [ r ] = m i n ( d p [ l ] [ r ] , d p [ l ] [ k ] + d p [ k + 1 ] [ r ] ) , 如 果 不 能 折 叠 就 枚 举 折 叠 断 点 \begin{cases} dp[l][r] = min(dp[l][r], dp[l][k] + 2 + num(整串长/折叠因子长)), 如果能被折叠\\[2ex] dp[l][r] = min(dp[l][r], dp[l][k] + dp[k+1][r]), 如果不能折叠就枚举折叠断点 \end{cases} ⎩⎨⎧dp[l][r]=min(dp[l][r],dp[l][k]+2+num(整串长/折叠因子长)),如果能被折叠dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]),如果不能折叠就枚举折叠断点
attention: 转移, 区间
双倍经验: 好题! 经典, 重点是判断折叠函数
/*
* 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 = 9e2 + 9;
const int mod = 19650827;
using namespace std;
int dp[maxn][maxn], n;
string tmp;
bool is_fold(int l, int r, int L, int R) //判断区间l, r 区间是否能被[l, k]折叠
{
if ((R - l + 1) % (r - l + 1))
return 0;
int len = r - l + 1;
_rep(i, L, R)
{
if (tmp[i - 1] != tmp[i - len - 1])
return 0;
}
return 1;
}
int cal(int x)
{
int res = 0;
while (x)
{
x /= 10;
res++;
}
return res;
}
int main()
{
ios::sync_with_stdio(0);
cin >> tmp;
n = tmp.size();
_rep(i, 1, n) dp[i][i] = 1;
_rep(len, 2, n)
{
_rep(l, 1, n - len + 1)
{
int r = l + len - 1;
dp[l][r] = len;
_for(k, l, r)
{
if (!is_fold(l, k, k + 1, r))
{
dp[l][r] = min(dp[l][k] + dp[k + 1][r], dp[l][r]);
}
else
{
dp[l][r] = min(dp[l][r], 2 + dp[l][k] + cal(len / (k - l + 1)));
}
}
}
}
cout << dp[1][n] << endl;
}