[CF825F] String Compression 题解

题意

给定一个字符串 S S S,其中出现循环的子串压缩后长度为:循环节出现次数十进制下的位数+循环节长度,无循环的串也需要压缩。求压缩后的最小长度。

思路

对于串 T T T,对其执行 KMP 算法后得到 n x t nxt nxt 数组,可能的最小循环节长度 l e n len len 即为 ∣ T ∣ − n x t ∣ T ∣ |T|-nxt_{|T|} TnxtT,其中 ∣ T ∣ |T| T 表示 T T T 的长度;如果 l e n ∤ ∣ T ∣ len\nmid |T| lenT T T T 中无小的循环节(即循环节长度为 ∣ T ∣ |T| T)。对 S S S 串进行压缩可以认为是把 S S S 分成若干个子串,这些子串全部完全压缩。

考虑 dp。设 f i f_i fi 表示将 S S S 串的前 i i i 个字符进行压缩的最小长度。初始 f i = i + 1 f_i=i+1 fi=i+1。转移方程即为: f i ← min ⁡ { f j + c n t j + 1 , i }   ( 1 ≤ j < i ≤ ∣ S ∣ ) f_i\gets\min\{f_j+cnt_{j+1,i}\}\ (1\le j<i\le |S|) fimin{fj+cntj+1,i} (1j<iS),其中 c n t x , y cnt_{x,y} cntx,y 表示将 S S S [ x , y ] [x,y] [x,y] 这个部分完全压缩后的长度,求法即为:对于每个 1 ≤ i ≤ ∣ S ∣ 1\le i\le |S| 1iS,都将 S S S 的前 i − 1 i-1 i1 个字符删除后跑 KMP,求得的 ( j − i + 1 ) − n x t j   ( i ≤ j ≤ ∣ S ∣ ) (j-i+1)-nxt_j\ (i\le j\le |S|) (ji+1)nxtj (ijS) 即为 c n t i , j cnt_i,j cnti,j,注意判断是否能够整除。时间复杂度 O ( n 2 ) 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;
}
  • 16
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值