CodeForces 825F String Compression---这个KMP很DP

本文介绍了一种针对长字符串的高效压缩算法,通过寻找最小循环节来实现字符串的有效压缩。利用KMP算法确定循环节,并采用动态规划策略计算最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ivan wants to write a letter to his friend. The letter is a string s consisting of lowercase Latin letters.

Unfortunately, when Ivan started writing the letter, he realised that it is very long and writing the whole letter may take extremely long time. So he wants to write the compressed version of string s instead of the string itself.

The compressed version of string s is a sequence of strings c1, s1, c2, s2, ..., ck, sk, where ci is the decimal representation of number ai (without any leading zeroes) and si is some string consisting of lowercase Latin letters. If Ivan writes string s1exactly a1 times, then string s2 exactly a2 times, and so on, the result will be string s.

The length of a compressed version is |c1| + |s1| + |c2| + |s2|... |ck| + |sk|. Among all compressed versions Ivan wants to choose a version such that its length is minimum possible. Help Ivan to determine minimum possible length.

Input

The only line of input contains one string s consisting of lowercase Latin letters (1 ≤ |s| ≤ 8000).

Output

Output one integer number — the minimum possible length of a compressed version of s.

Example
Input
aaaaaaaaaa
Output
3
Input
abcab
Output
6
Input
cczabababab
Output
7
Note

In the first example Ivan will choose this compressed version: c1 is 10s1 is a.

In the second example Ivan will choose this compressed version: c1 is 1s1 is abcab.

In the third example Ivan will choose this compressed version: c1 is 2s1 is cc2is 1s2 is zc3 is 4s3 is ab.



题意:给一个字符串,求它的最小循环节的长度和最大循环数目位数之和。


思路:KMP自带求循环节的功能,如果不知道,看这里-->http://www.cnblogs.com/jackge/archive/2013/01/05/2846006.html

题目中字符长度不超过8000,可以直接暴力求解i位置开始长度为j的字符串的循环节,每次取得最小的循环节,用dp[i+j]定义从i位置开始长度为j的字符串(即:i+j位置结尾的字符串)的最小循环节长度与最大循环次数的位数之和这个子状态,

状态转移方程:dp[j+i] = min(dp[j+i], dp[i]+cnt[j/xlen]+xlen):dp[i]表是i位置结尾的字符串最小循环节长度与最大循环数目的最小和,cnt[j/xlen]表示最大循环次数的位数,xlen表示最小循环节长度;

特别注意当j%xlen!=0时,说明没有循环节可寻,此时最大循环位数自然为1,最小循环长度就是j.


# include <bits/stdc++.h>
using namespace std;
const int maxn = 8800;
char s[maxn], buf[20];
int cnt[maxn], dp[maxn]={0}, pre[maxn];
void kmp(char *s, int len, int pre[])
{
      pre[0] = pre[1] = 0;
    for(int i=1; i<len; ++i)
    {
        int j = pre[i];
        while(j && s[i]!=s[j]) j=pre[j];
        pre[i+1] = s[j]==s[i]?j+1:0;
    }
}
int main()
{
    scanf("%s",s);
    int len = strlen(s);
    for(int i=1; i<=len; ++i)
        cnt[i] = sprintf(buf, "%d", i), dp[i] = i+1;//sprintf的返回值是字符的长度
    for(int i=0; i<len; ++i)
    {
        kmp(s+i,len-i,pre);
      // cout<<s+i<<endl;
        for(int j=1; j+i<=len; ++j)
        {
            int xlen = j-pre[j];
            //cout<<j<<"--"<<xlen<<endl;//i为起位置,j为从i开始的字符串长度,xlen表示该字符串循环节最小循环节长度。
           // cout<<i+j<<endl;
            if(j%xlen == 0)
                dp[j+i] = min(dp[j+i], dp[i]+cnt[j/xlen]+xlen),cout;
            else dp[j+i] = min(dp[j+i], dp[i]+1+j);
        }
    }
    printf("%d\n",dp[len]);
    return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值