思路:
用kmp的做法先求出这个字符串的nxt[](最长相同前缀后缀)。
假设有这样一个字符串,nxt=2,这个字符串的最大周期就是6,最小周期就是5:
XX XX YY YY YY XX XX
| 最大周期=6 |
| 最小周期=5 | nxt=2 |
从最小周期来看,我们可以看出最小周期长==nxt。
因为两个最小周期和原串比就是:
XX XX XX XX XX XX XX XX (两个最小周期)
XX XX XX XX XX XX (原串)
| A | B |
A部分为原来的最小周期。因为两串的A+B部分是完全相同的,由于最小周期是原串的一个前缀,所以A部分也一定相同,相减得到B部分也定相同。而在最小周期中看,B部分是原串的一个前缀;在原串中看,B又是原串的一个后缀。所以一个周期的长度=原串长 - 某一个相同前缀后缀的长。那么最小周期长=原串长 - 最长相同前缀后缀的长=原串长-nxt。
同理:最大周期=原串长 - 最短相同前后缀的长。
最短相同前后缀长怎么求呢?
设t[]为最短相同前缀后缀长。
可以递推求解:t[i] = t[ nxt[i] ] , i∈[0,len-1] ,说汉语就是 原串的最长相同前后缀的最短相同前后缀 也就是 原串的最短相同前后缀。i正着从0开始循环,所以可以保证t[nxt[i]]的值是已经求过了的。
特别的,当nxt[i]=-1时,t[i]=i。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000000
#define ll long long
int n;
char a[maxn+5];
int nxt[maxn+5];
int t[maxn+5];
int main(){
scanf("%d",&n);
scanf("%s",a);
nxt[0]=-1;
for(int i=1;i<n;i++){
int j=nxt[i-1];
while(a[j+1]!=a[i]&&j>=0) {
j=nxt[j];
}
if(a[j+1]==a[i]) nxt[i]=j+1;
else nxt[i]=-1;
}
for(int i=0;i<n;i++){
if(nxt[i]==-1) t[i]=i;
else t[i]=t[nxt[i]];
}
ll ans=0;
for(int i=0;i<n;i++){
if(~t[i]) ans+=i-t[i];
}
printf("%lld",ans);
return 0;
}