JZOJ 4676. 【NOIP2016提高A组模拟7.21】模板串

Description

科学家温斯顿从数据库中找到了一串相当长的字符串。
他正试图用一个模板串来重构这个字符串。
他可以将模板串复制多份,通过合适的方式拼接起来,使得最终的串与原串一致。
如果两个模板串互相覆盖,那么覆盖的部分必须完全一致。
原串的所有位置必须被覆盖到。
显然,原串本身就是一个模板串。但为了节省成本,他想找到长度最短的模板串。

Input

第一行一个仅由小写字母构成的字符串。

Output

第一行一个整数,表示模板串的最小长度。

Sample Input

输入1:

ababa

输入2:

ababbababbabababbabababbababbaba

Sample Output

输出1:

3

输出2 :

8

Data Constraint

设字符串的长度为N。
Subtask1[20pts]:N<=100
Subtask2[30pts]:N<=25000
Subtask3[50pts]:N<=500000

Solution

  • 我们设 F[i] 表示能覆盖前缀 i 最短 的前缀。

  • 可以发现 F[i] 只有两种取值: F[next[i]] i

  • 因为能成为答案的前缀 只可能 同时是 i 的前缀和后缀。

  • 然后考虑什么条件下 F[i] 能等于 F[next[i]]

  • 可以发现最后接上的一段最长是 next[i]

  • 如果这一段能被 F[next[i]] 覆盖的话,这一段前面那个前缀也必须能被 F[next[i]] 覆盖。

  • 因为 F[next[i]] 满足自身不能再被覆盖。

  • 所以我们直接在区间 [inext[i]i] 中寻找是否有一个 j 满足 F[j]=F[next[i]]

  • 若存在则 F[i] 可等于 F[next[i]]

  • 实现的话开一个桶, h[i] 表示 F[j] 值为 i 的最大的 j ,就可以做到 O(n) 了。

Code

#include
#include
using namespace std;
const int N=500005;
int next[N],f[N],h[N];
char s[N];
int main()
{
    scanf("%s",s+1);
    int n=strlen(s+1);
    for(int i=2,j=0;i<=n;i++)
    {
        while(j && s[i]!=s[j+1]) j=next[j];
        if(s[i]==s[j+1]) j++;
        next[i]=j;
    }
    for(int i=1;i<=n;h[f[i]]=i++)
    {
        int x=f[next[f[i]=i]];
        if(h[x]>=i-next[i]) f[i]=x;
    }
    printf("%d",f[n]);
}

你可能感兴趣的:(动态规划,KMP,字符串)