P3435 [POI2006]OKR-Periods of Words
P3435 [POI2006]OKR-Periods of Words
卡了好久,一直不敢确认思路是对的。。。
这题非常显然可以转化成“对于每一个 \(S_{1,\cdots,i}\) 求出最短公共前后缀 \(q_j\)”,那么 \(ans=\sum i-q_i\)
首先求出 \(KMP\) 的 \(next\) 数组,那么最短公共前后缀就是在 \(next\) 不为 \(-1\) 的情况下不断跳。
每次暴力跳的复杂度是不对的,实测 TLE 74
考虑类似并查集的路径压缩进行优化因为中间经过的路径能压缩就压缩,不影响答案(当然也可以连边 \(next_i\to i\) ,跑DAG)
均摊一下复杂度还是 \(O(n)\)
另外,教练说字符串下标从零开始是一种信仰,于是我就改了。。。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define x first
#define y second
#define sz(v) (int)v.size()
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=0;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
return f?x:-x;
}
#define N 1000005
int n,P[N];
char str[N];
LL ans;
signed main(){
scanf("%d%s",&n,str);
int j=-1;P[0]=-1;
for(int i=1;i<n;++i){
while(~j&&str[j+1]!=str[i])j=P[j];
if(str[j+1]==str[i])++j;
P[i]=j;
}
for(int i=0;i<n;++i){
j=P[i];
while(~j&&~P[j])P[i]=j=P[j];
if(~j)ans+=i-j;
}
printf("%lld\n",ans);
return 0;
}
路漫漫其修远兮,吾将上下而求索