首页 > 代码库 > BZOJ1090: [SCOI2003]字符串折叠

BZOJ1090: [SCOI2003]字符串折叠

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1090

Description

折叠的定义如下: 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。

Input

仅一行,即字符串S,长度保证不超过100。

Output

仅一行,即最短的折叠长度。

Sample Input

NEERCYESYESYESNEERCYESYESYES

Sample Output

14

HINT

一个最短的折叠为:2(NEERC3(YES))

题解:这道题还是老规矩啦,设f[i][j]表示区间i~j能转换成的最短长度,然后再枚举一个k,分成两半,计算最小值,不过有些不同的是这次我们要判断区间i~j是否能被折叠为用i~k来表示,如果能的话,计算折叠后的长度是否短于之前的长度,如果短于,记录下来,我开始看到是字符串的时候还以为超级难,结果看懂后。。。。。

程序:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<string>
using namespace std;
int tot,n,j,lent,f[200][200];
char s[200];
string s1;
bool check(int x,int y,int z)
{
  tot=(y-x+1)/(z-x+1);//增加的数字的长度
  lent=0;
  for (int i=x;i<=y;i++)
    if (s[i]!=s[(i-x)%(z-x+1)+x]) return false;//这个我写的有点乱,其实就是判断是否能被折叠的,可以自己推一下,或者写的更清楚一点,我就不解释了,
  while (tot>0)
  {
      lent+=1;
      tot/=10;
  }//计算数字的长度
  return true;//返回值
}
int main()
{
  cin>>s1;
  n=s1.size();
  for (int i=1;i<=n;i++) s[i]=s1[i-1];//还是存到字符数组里方便些
  for (int i=1;i<=n;i++) f[i][i]=1;
  for (int len=2;len<=n;len++)
    for (int i=1;i<=n-len+1;i++)
    {
      j=i+len-1;
      f[i][j]=j-i+1;//最开始的长度
      for (int k=i;k<=j-1;k++)
      {
          if (len%(k-i+1)==0)//如果i~j的区间长度刚好能整除i~k的长度的话
          if (check(i,j,k))//检查是否能被折叠 
          {
          f[i][j]=min(f[i][j],f[i][k]+lent+2);//lent是指那个增加的数字的长度,2是指括号
        }
          f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);//不能被折叠的话,分成两半,来找最小值
      }
    }
  cout<<f[1][n]<<endl;//输出答案
  return 0;
}

 

BZOJ1090: [SCOI2003]字符串折叠