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; }
蒟蒻总是更懂你✿✿ヽ(°▽°)ノ✿