KR-Periods of Words(Bzoj1511)

 

试题描述
串是有限个小写字符的序列,特别的,一个空序列也可以是一个串。一个串 P 是串 A 的前缀,当且仅当存在串 B,使得 A=PB。如果P≠A并且 P 不是一个空串,那么我们说 P 是 A 的一个 proper 前缀。
定义 Q 是 A 的周期,当且仅当 Q 是 A 的一个 proper 前缀并且 A 是 QQ 的前缀(不一定要是 proper 前缀)。比如串 abab 和 ababab 都是串 abababa 的周期。串 A 的最大周期就是它最长的一个周期或者是一个空串(当 A 没有周期的时候),比如说,ababab 的最大周期是 abab。串 abc 的最大周期是空串。
给出一个串,求出它所有前缀的最大周期长度之和。
输入
第一行一个整数 k(1<k<10^6),表示串的长度。
接下来一行表示给出的串。
输出
输出一个整数表示它所有前缀的最大周期长度之和。
输入示例
8
babababa
输出示例
24
其他说明
数据范围与提示
对于全部数据,1<k<10^6。

 


这道题目的主要任务就是把题目读懂

大概的意思就是是给定一个字符串,把这个字符串的所有前缀的最长循环节的长度相加

循环节的定义与平时不同,要求A是QQ的前缀

例如abab,他的最长循环节就是ab,而不是aba,因为abab不是abaaba的前缀

下面来看一下具体如何处理,我们这需要对如何用KMP的nxt数组求循环节有一定了解

这和Bzoj1355有类似之处,可以先去看一看那道题(我的博客)

https://www.cnblogs.com/WWHHTT/p/9706517.html

知道了如何求出循环节,我们来看这两道题有什么区别,首先是对于每个前缀都要处理(比较好解决)

其次,前缀的的定义不同,要求最长,并且两个不同部分可以有重合

循环节还要求最长,原本的式子是    最短周期=总长度-nxt[i]    i为当前的下标

求最长只需要加一个递推即可,我们要使nxt[i]的值最小,也就是求出这个后缀的nxt的nxt的nxt……

只要一个递推就OK了

下面给出代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
using namespace std;
inline int rd(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
    return x*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10+'0');
    return ;
}
int p[1000006];
int f[1000006];
char s[1000006];
int n;
void pre(){//kmp板子 
    p[0]=0;
    int j=0;
    for(int i=1;i<n;i++){
        while(j>0&&s[i+1]!=s[j+1]) j=p[j];
        if(s[i+1]==s[j+1]) j++;
        p[i+1]=j;
    }
    return ;
}
int main(){
    n=rd();
    scanf("%s",s+1);
    pre();
    long long ans=0;
    for(int i=1;i<=n;i++){
        if(p[i]) f[i]=f[p[i]];//f[i]存的是长度为p[i]的前缀的最小nxt值 
        else f[i]=i;//如果没有nxt,也就没有循环节 
        ans+=i-f[i];
    }
    printf("%lld",ans);
    return 0;
}

 

posted @ 2018-09-26 15:23  Bruce--Wang  阅读(370)  评论(0编辑  收藏  举报