首页 > 代码库 > codeforces 825F F. String Compression dp+kmp找字符串的最小循环节

codeforces 825F F. String Compression dp+kmp找字符串的最小循环节

/**
题目:F. String Compression
链接:http://codeforces.com/problemset/problem/825/F
题意:压缩字符串后求最小长度。
思路:
dp[i]表示前i个字符需要的最小次数。
dp[i] = min(dp[j]+w(j+1,i)); (0<=j<i);

[j+1,i]如果存在循环节(自身不算),那么取最小的循环节x。w = digit((i-j)/x)+x;
否则w = i-j+1;

求一个区间最小循环节:
证明:http://www.cnblogs.com/chenxiwenruo/p/3546457.html
KMP最小循环节、循环周期:

定理:假设S的长度为len,则S存在最小循环节,循环节的长度L为len-next[len],子串为S[0…len-next[len]-1]。

如果next[len]==0,T = len/L = 1; 说明自身就是循环节。周期为1.

(1)如果len%(len - next[len])==0,则表明字符串S可以完全由循环节循环组成,循环周期T=len/L。(这里的len就是从0开始的len位置,kmp也计算了next[len]。)

(2)如果不能,说明还需要再添加几个字母才能补全。需要补的个数是循环个数L-len%L=L-(len-L)%L=L-next[len]%L,L=len-next[len]。
*/

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <iostream>
#include <vector>
using namespace std;
#define ms(x,y) memset(x,y,sizeof x)
const int N = 8e3 + 10;
const int INF = 0x3f3f3f3f;
char s[N];
int f[N];
int dp[N];
int w[N][N];///w[i][j]表示[i,j]字符串的价值。
int digit(int x)
{
    int cnt = 0;
    while(x){
        cnt++;
        x /= 10;
    }
    return cnt;
}
void getFail(char *p,int* f)
{
    int m = strlen(p);
    f[0] = f[1] = 0;
    for(int i = 1; i < m; i++){
        int j = f[i];
        while(j&&p[i]!=p[j]) j = f[j];
        f[i+1] = (p[i]==p[j])?j+1:0;
    }
}
void solve(char *s)
{
    int n = strlen(s);
    for(int i = 0; i < n; i++) w[i][i] = 2;
    for(int i = 0; i < n; i++){
        getFail(s+i,f);
        for(int j = i+2; j <= n; j++){
            int len = j-i;
            int d = len-f[j-i];
            if(f[j-i]==0||len%d!=0){
                w[i][j-1] = len+1;
            }else
            {
                w[i][j-1] = digit(len/d)+d;
            }
        }
    }
}
int main()
{
    while(scanf("%s",s)==1)
    {
        solve(s);
        int n = strlen(s);
        dp[0] = w[0][0];
        for(int i = 1; i < n; i++){
            dp[i] = w[0][i];///整段[0,i]。
            for(int j = 0; j < i; j++){
                dp[i] = min(dp[i],dp[j]+w[j+1][i]);
            }
        }
        printf("%d\n",dp[n-1]);
    }

    return 0;
}

 

codeforces 825F F. String Compression dp+kmp找字符串的最小循环节