[SCOI2003]字符串折叠
题目描述
折叠的定义如下:
- 一个字符串可以看成它自身的折叠。记作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。输入输出格式
输入格式
仅一行,即字符串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]作为循环节,那么他们都需要具备什么条件呢?
所以,我们就可以写出这样的代码对她们进行判断:
这里的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]
#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;
}
完~。