题意
给定一个字符串 SSS,其中出现循环的子串压缩后长度为:循环节出现次数十进制下的位数+循环节长度,无循环的串也需要压缩。求压缩后的最小长度。
思路
对于串 TTT,对其执行 KMP 算法后得到 nxtnxtnxt 数组,可能的最小循环节长度 lenlenlen 即为 ∣T∣−nxt∣T∣|T|-nxt_{|T|}∣T∣−nxt∣T∣,其中 ∣T∣|T|∣T∣ 表示 TTT 的长度;如果 len∤∣T∣len\nmid |T|len∤∣T∣ 则 TTT 中无小的循环节(即循环节长度为 ∣T∣|T|∣T∣)。对 SSS 串进行压缩可以认为是把 SSS 分成若干个子串,这些子串全部完全压缩。
考虑 dp。设 fif_ifi 表示将 SSS 串的前 iii 个字符进行压缩的最小长度。初始 fi=i+1f_i=i+1fi=i+1。转移方程即为:fi←min{fj+cntj+1,i} (1≤j<i≤∣S∣)f_i\gets\min\{f_j+cnt_{j+1,i}\}\ (1\le j<i\le |S|)fi←min{fj+cntj+1,i} (1≤j<i≤∣S∣),其中 cntx,ycnt_{x,y}cntx,y 表示将 SSS 中 [x,y][x,y][x,y] 这个部分完全压缩后的长度,求法即为:对于每个 1≤i≤∣S∣1\le i\le |S|1≤i≤∣S∣,都将 SSS 的前 i−1i-1i−1 个字符删除后跑 KMP,求得的 (j−i+1)−nxtj (i≤j≤∣S∣)(j-i+1)-nxt_j\ (i\le j\le |S|)(j−i+1)−nxtj (i≤j≤∣S∣) 即为 cnti,jcnt_i,jcnti,j,注意判断是否能够整除。时间复杂度 O(n2)O(n^2)O(n2)。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 8005;
char S[maxn]; int f[maxn],nxt[maxn];
int count(int x) { // 数出循环节十进制位数
int res = 0;
while (x) res ++, x /= 10;
return res;
}
int n;
void KMP(char S[]) { // 求出的 nxt 数组下标从起点开始
int len = strlen(S + 1);
memset(nxt,0,sizeof(nxt));
for (int i = 2,now = 0;i <= len;i ++) {
while (now && S[i] != S[now + 1]) now = nxt[now];
if (S[i] == S[now + 1]) now ++;
nxt[i] = now;
}
}
int main() {
scanf("%s",S + 1); n = strlen(S + 1);
for (int i = 1;i <= n;i ++)
f[i] = i + 1;
for (int i = 0;i <= n;i ++) {
KMP(S + i); // 以每个 i 为开头跑出 nxt 数组
for (int len = 1;i + len <= n;len ++) { // 枚举完全压缩的区间长度
int j = i + len;
if (len % (len - nxt[len]) == 0) // 存在循环节
f[j] = min(f[j],f[i] + count(len / (len - nxt[len])) + len - nxt[len]);
else f[j] = min(f[j],f[i] + len + 1); // 否则只能整个压缩
}
}
printf("%d",f[n]);
return 0;
}
文章描述了一种方法,通过KMP算法确定字符串中的循环节,然后利用动态规划计算以每个位置为起点的子串压缩后的最小长度,以达到整体字符串的最小压缩长度。

被折叠的 条评论
为什么被折叠?



