【KMP】洛谷_3435 OKR-Periods of Words

题意

给出一个字符串,对于给定串的每个前缀i,求最长的,使这个字符串重复两边能覆盖原前缀i的前缀(就是前缀i的一个前缀),求所有的这些“前缀的前缀”的长度和。

思路

因为我们这里要求重复两边覆盖掉原前缀 i ,那么我们就可以想到 n e x t 的前缀与后缀相等的性质。
举个例子: a   b   c   c   a   b
其中前缀[ 5 ]: A { a   b   c   c   a }中的 n e x t [ 5 ] = 1 ,说明了 A [ 1 ~ 1 ] = A [ 5 ~ 5 ],我们就可以让 A [ 1 ~ 4 ]重复两边,变成
{ a   b   c   c   a   b   c   c },覆盖掉了前缀[ 5 ],那么答案就累加 5 n e x t [ 5 ]= 4
我们找到一个最长的前缀i的前缀,那么我们就让 n e x t 一直往前跳,找到一个最前面的 n e x t ,然后让答案累加 i n e x t ,代表我们把[ 1 ~ i n e x t ]复制两边,最长的前缀就是 i n e x t 了。
有些数据可能会给出相同的数,所以我们每次记忆化一下。

代码

#include<cstdio>
int n,next[1000001];
long long ans;
char s[1000001];
void jump(int x) {
    int r=x;
    while (next[x]) {
        ans+=x-next[x];//累加答案
        x=next[x];
    }
    if (next[r]!=0) next[r]=x;//记录一下跳到哪了
}
int main() {
    scanf("%d",&n);
    scanf("%s",s+1);
    for (int i=2,j=0;i<=n;i++) {//求出next
        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;i++) 
        jump(i);
    printf("%lld",ans);
}

猜你喜欢

转载自blog.csdn.net/SSL_hzb/article/details/81782612