P4840 P哥旋转 题解
思路
前置知识:回文自动机。
首先破环为链,复制一份
在加入一个字符时,通过跳一遍 fail 找到所有以它结尾的回文串,将这些点加一,然后我们需要在
注意,我们只计算长度小于等于
注意到数据随机,所以 fail 树上的点深度基本都很低,于是复杂度不会很高。
代码
#include<bits/stdc++.h>
using namespace std;
constexpr int N=3e6+5;
int n,len[N],cnt[N],fail[N],trie[N][26],tot=1;
string s;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > Q;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>s,n=s.size(),s='$'+s+s;
len[0]=0,fail[0]=1;
len[1]=-1,fail[1]=0;
int ans=0,last=1,sub=0;
for(int i=1;i<=2*n-1;i++){
while(!Q.empty()&&Q.top().first+n-1<i){
int q=Q.top().second;
cnt[q]--;
if(cnt[q]==0)sub--;
Q.pop();
}
int fa=last;
while(len[fa]>n-2||s[i]!=s[i-len[fa]-1])fa=fail[fa];
int &v=trie[fa][s[i]-'a'];
if(!v){
++tot;
int tmp=fail[fa];
while(s[i]!=s[i-len[tmp]-1])tmp=fail[tmp];
tmp=trie[tmp][s[i]-'a'];
v=tot;
fail[v]=tmp;
len[v]=len[fa]+2;
}
for(int q=v;q>=2;q=fail[q]){
if(cnt[q]==0)sub++;
cnt[q]++;
Q.push(make_pair(i-len[v]+1,q));
}
last=v;
ans=max(ans,sub);
}
cout<<ans;
return 0;
}