该题与一般的区间dp有点区别在于我们的dp值是由该序列可否折叠得来,与一般的动态规划的转移方程是不一样的,这题区间的dp值体现从i到j最小的折叠数,可能这个折叠有1也就是不折叠,或者2,3,4取决于这个序列的长度,所以我们的dp值是由更小的序列最优值得来的,所以很明显我们需要使用区间dp的思想。
// 字符串折叠.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 200;
string s;
int d[maxn][maxn];
bool check(string s,int n,int len)//该函数判断这个序列是否可以折叠len的长度
{
for (int i = len;i < n;i++)
{
if (s[i] != s[i%len])return false;//我们每次与序列的前len个项进行对比,将序列后面几个len值全部比较完毕,如果有一个不符合,
//那么直接返回false
}
return true;
}
int main()
{
cin >> s;
memset(d, 0x3f, sizeof(d));
for (int i = 0;i < s.length();i++)
{
d[i][i] = 1;//很明显自己和自己可以折叠变成1
}
for (int length = 2;length <= s.length();length++)//区间dp的固定套路
{
for (int i = 0,j = i + length - 1;j<s.length();j++,i++)//这里我们使用了string,所以起止位置是从0开始的
{//相应的i与j的写法就要有相应的区别
for (int k = i;k < j;k++)
{
d[i][j] = min(d[i][j], d[i][k] + d[k + 1][j]);
}
for (int k = i;k < j;k++)//这就是与我之前做过的区间dp不一样的地方,我们需要看这一个序列里的各段(长度可以有1-n),
//后面有判断,是否可以折叠,然后折叠长度的最小值
{
int len = k-i + 1;
if (length%len != 0)
{//看看能不能len长度将序列整分
continue;
}
if (check(s.substr(i), length, len))//小序列起点位置不一定在0,所以起点是i开始,len是将序列分为len长度
//length也就是序列的最大值
{
int temp = (j - i + 1) / (k - i + 1);//前面要加一个数表明序列分段的段数,可能有两位数三位数
if (temp / 100 != 0)
{
temp = 3;
}
else if (temp / 10 != 0)
{
temp = 2;
}
else
{
temp = 1;
}
d[i][j] = min(d[i][j], d[i][k] + 2 + temp);//这才是状态转移方程
}
}
}
}
cout << d[0][s.length() - 1];
}