1 条题解
-
0
题目分析
本题要求将一个由大写字母 'A' 到 'Z' 组成的序列,折叠成字符数最少的折叠序列。题目定义了三种折叠规则: 单个字符:单个大写字母本身就是一种简单的折叠序列,展开后为其自身,这是折叠序列的最基本形式。 连接操作:若有两个折叠序列 S 和 Q,将它们连接起来形成的 SQ 也是折叠序列,其展开结果为 S 和 Q 各自展开后的连接,这使得可以将多个部分的折叠结果组合起来。
重复操作:
当 S 是折叠序列,X 是大于 1 的整数时,X (S) 构成折叠序列,展开后是 S 重复 X 次,这是实现对重复子序列压缩折叠的关键操作 。
解题思路
暴力枚举所有可能的子序列:通过两层循环遍历字符串,确定所有可能的子序列范围。外层循环控制子序列的起始位置,内层循环控制子序列的结束位置。对于每个确定的子序列,检查它在整个字符串中重复出现的次数。
判断子序列是否重复出现:在确定子序列后,从字符串开头开始,每次以子序列的长度为步长,检查后续相同长度的子串是否与该子序列相等。若连续出现多次相同子序列,则记录其重复次数。
计算折叠后的长度:对于每个找到的重复子序列,计算将其折叠后的长度,即重复次数的数字位数加上子序列本身长度加上括号的长度(2)。同时计算不折叠时的长度,即子序列重复出现的总长度。比较折叠前后的长度,若折叠后长度更短,则使用折叠后的表示替换原字符串中的重复子序列部分。
处理非重复子序列:对于未找到重复的子序列,即单个字符或不重复的子串,保持其原样。 迭代优化:对字符串进行多次上述操作,因为一次折叠后可能会产生新的可折叠子序列。直到无法再找到可以进一步折叠以缩短字符串长度的子序列为止,此时得到的字符串即为最短的折叠序列。
#include<cstring> #include<algorithm> using namespace std; int n,nd[105],f[105][105],pre[105][105],nxt[105][105]; char s[105]; void print(int L,int R) { if(L==R) putchar(s[L]); else if(pre[L][R]>0) { print(L,pre[L][R]); print(pre[L][R]+1,R); } else { printf("%d(",-(R-L+1)/pre[L][R]); print(L,L-pre[L][R]-1); putchar(')'); } } int main() { #ifdef local freopen("pro.in","r",stdin); #endif for(int i=2;i<=9;i++) nd[i]=1; for(int i=10;i<=99;i++) nd[i]=2; nd[100]=3; scanf("%s",s+1); n=strlen(s+1); memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;i++) f[i][i]=1; for(int bg=1;bg<=n;bg++) { nxt[bg][0]=0; for(int i=2,j=0;i<=n-bg+1;i++) { while(j>0&&s[bg+i-1]!=s[bg+j]) j=nxt[bg][j-1]; if(s[bg+i-1]==s[bg+j]) j++; nxt[bg][i-1]=j; } } for(int len=2;len<=n;len++) for(int i=1;i+len-1<=n;i++) { int j=i+len-1; int &p=pre[i][j]; p=i; for(int k=i+1;k<j;k++) if(f[i][p]+f[p+1][j]>f[i][k]+f[k+1][j]) p=k; f[i][j]=f[i][p]+f[p+1][j]; int v=len-nxt[i][len-1]; if(len%v==0) for(int k=1;v*k<=len;k++) if(len%(v*k)==0&&len/(v*k)>1&&f[i][i+v*k-1]+2+nd[len/(v*k)]<f[i][j]) { f[i][j]=f[i][i+v*k-1]+2+nd[len/(v*k)]; p=-v*k; } } print(1,n); puts(""); return 0; }
- 1
信息
- ID
- 1177
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 4
- 已通过
- 1
- 上传者