洛谷P4302 [SCOI]字符串折叠 [字符串,区间DP]

  题目传送门

字符串折叠

题目描述

折叠的定义如下:

  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))


  分析:

  康战士的考试里面出的题目。考的时候直接弃疗。。。(先膜一波老余AK%%%)

  需要用区间DP来做,首先定义动规数组f[l][r],表示从l到r这一段区间内的字符串折叠后能得到的最短结果。那么枚举折叠的区间,然后枚举左右区间,再枚举可折叠的长度,也就是枚举区间长度的所有因数,然后进行判断该区间是否可以折叠,如果可以则进行状态转移。值得注意的是,转移完以后还需要在进行依次断点枚举,表示将该区间分成两次折叠,看能否得到最短折叠。当然,蒟蒻不擅长动规,还是听了老余讲课,又参考了大佬的博客才弄懂的。如果上面的思路不太懂,就直接看代码吧,代码好懂多了。

  Code:

 1 //It is made by HolseLee on 28th May 2018
 2 //Luogu.org P4302
 3 #include<bits/stdc++.h>
 4 using namespace std;
 5 char s[107];int f[107][107];
 6 inline bool check(int l,int r,int k)
 7 {
 8   for(int i=l+k,p=0;i<=r;i++,p=(p+1)%k)
 9     if(s[i]!=s[l+p])return false;
10   return true;
11 }
12 inline int get(int x)
13 {int ret=2;while(x)x/=10,ret++;return ret;}
14 int main()
15 {
16   freopen("char.in","r",stdin);
17   freopen("char.out","w",stdout);
18   memset(f,0x7f,sizeof(f));
19   scanf("%s",s+1);int n=strlen(s+1);
20   for(int i=1;i<=n;i++)f[i][i]=1;
21   for(int i=2;i<=n;i++)
22     for(int l=1;l+i-1<=n;l++){
23       int r=l+i-1;
24       for(int k=1;k*k<=i;k++){
25     if(i%k==0){
26       if(check(l,r,i/k))f[l][r]=min(f[l][r],f[l][l+i/k-1]+get(k));
27       if(check(l,r,k))f[l][r]=min(f[l][r],f[l][l+k-1]+get(i/k));}}
28       for(int k=l;k<=r;k++)
29     f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]);}
30   printf("%d",f[1][n]);return 0;
31 }

猜你喜欢

转载自www.cnblogs.com/cytus/p/9099792.html