[SCOI2003]字符串折叠(区间DP)

题目描述

[SCOI2003]字符串折叠
题目描述
折叠的定义如下:

  1. 一个字符串可以看成它自身的折叠。记作S = S
  2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S) = SSSS…S(X个S)。
  3. 如果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。

输入输出格式
输入格式
仅一行,即字符串S,长度保证不超过100。
输出格式
仅一行,即最短的折叠长度。
输入输出样例
输入样例 #1
NEERCYESYESYESNEERCYESYESYES
输出样例 #1
14
说明
一个最短的折叠为:2(NEERC3(YES))


思路

我觉得这道题最巧妙的地方就是循环节的处理,可以思考一下,如果给你一个字符串的话,那么你要怎么处理这个字符串是否有循环节?如果有的话,那么循环节应该是什么?长度又会是多少呢?

之后,我们再来看一下这一道题,我们就可以发现,这道题就是我上面问题的一点点改编,或者是把难度降低了一些的低配。

我们思考一下如何设置状态

按照区间DP的一般套路: f [ i , j ] f[i,j] f[i,j]表示的是区间 [ i , j ] [i,j] [i,j]的最小的表示字符串的长度。

考虑转移:那么我们考虑一个区间会有什么情况呢?

一个区间是可以由两个区间直接摞到一起的结果,也可以是区间中的一个循环节不断循环的结果,那么,我们的状态转移也是可以用这两种方式转移而来的。

那么,我们就可以枚举断点 k k k进行上述的两种操作。

f [ i , j ] = m i n ( f [ i , j ] , f [ i , k ] + f [ k + 1 , j ] ) f[i,j]=min(f[i,j], f[i,k]+f[k+1,j]) f[i,j]=min(f[i,j],f[i,k]+f[k+1,j])

如果 [ i , j ] [i,j] [i,j]是以 [ i , k ] [i,k] [i,k]作为循环节的话:
f [ i , j ] = m i n ( f [ i , j ] , 2 + f [ i , k ] + n u m [ l e n i , j l e n i , k ] ) f[i,j]=min(f[i,j], 2+f[i, k]+num[\frac{len_{i,j}}{len_{i,k}}]) f[i,j]=min(f[i,j],2+f[i,k]+num[leni,kleni,j])
n u m [ i ] num[i] num[i]表示的是数字 i i i的数字位数,这里的2指的是左右的括号的数量。

那么,这道题目最精彩的地方就要来了:我们要如何判断 [ i , j ] [i,j] [i,j]是以 [ i , k ] [i,k] [i,k]作为循环节呢??

考虑一下如果 [ i , j ] [i,j] [i,j]是以 [ i , k ] [i,k] [i,k]作为循环节,那么他们都需要具备什么条件呢?

  1. 两个字符串的长度要成倍数关系;
  2. [ i , j ] [i,j] [i,j]中的字符必须满足条件和 [ i , k ] [i,k] [i,k]之间的字符按照顺序对应一一对应的关系即可。

所以,我们就可以写出这样的代码对她们进行判断:
这里的len是 [ i , k ] [i,k] [i,k]的长度, l , r l,r l,r也就是我们上面所说到的 [ i , j ] [i,j] [i,j]的边界。

bool check(int l,int r,int len)
{
	for(int i=l;i<=r;++i)
		if(s[i]!=s[(i-l)%len+l])	return false;
	return true;
}

我们的初始状态就应该是 f [ i , i ] = 1 f[i,i]=1 f[i,i]=1,显然当字符串中只有一个字符的时候,最小的长度就是 1.
目标状态就是: f [ 0 , s . s i z e ( ) − 1 ] f[0,s.size()-1] f[0,s.size()1]


code

#include
using namespace std;

const int nn=103;

string s;
int num[nn], f[nn][nn];

bool check(int l,int r,int len)
{
	for(int i=l;i<=r;++i)
		if(s[i]!=s[(i-l)%len+l])	return false;
	return true;
}

int main()
{
	cin>>s; memset(f,0x3f,sizeof(f));
	for(int i=1;i<=9;++i)	num[i]=1;
	for(int i=10;i<=99;++i)	num[i]=2;
	num[100]=3;
	for(int i=0;i<s.size();++i)	f[i][i]=1;
	for(int l=2;l<=s.size();++l)
		for(int i=0;i<s.size();++i)
		{
			int j=i+l-1;
			if(j>=s.size())	break;
			for(int k=i;k<s.size();++k)	f[i][j]=min(f[i][j], f[i][k]+f[k+1][j]);
			for(int k=i;k<s.size();++k)
			{
				int len=k-i+1;
				if(l%len==0)
					if(check(i, j, len))	f[i][j]=min(f[i][j], 2+f[i][k]+num[l/len]);
			}
		}
	cout<<f[0][s.size()-1]<<endl;
	return 0;
}

完~。

你可能感兴趣的:(动态规划————区间DP)