bzoj3676/洛谷3649 [Apio2014]回文串
题意:找到出现次数 ∗ ∗ 长度最大的回文子串
方法一:通过manacher找到回文串,然后到SAM中搜有多少个…..
然而…会t….
而我们会发现在SAM中搜有多少个,也就是找到这个回文串的最后一个字符,它的size
所以倍增直接找到最后一个点就可以了
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 300010
#define ll long long
char str[N];ll ans=0;
int root,last,cnt=0,n,len[N<<1],size[N<<1],fa[N<<1],son[N<<1][26],c[N<<1],a[N<<1],mx[N<<1],d[N<<1],f[N<<1][20],pos[N];
inline void ins(int ch,int id){
int p=last,np=++cnt;last=np;len[np]=len[p]+1;size[np]++;pos[id]=cnt;
while(p && !son[p][ch]) son[p][ch]=np,p=fa[p];
if(!p) fa[np]=root;
else{
int q=son[p][ch];
if(len[q]==len[p]+1) fa[np]=q;
else{
int nq=++cnt;len[nq]=len[p]+1;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
while(son[p][ch]==q) son[p][ch]=nq,p=fa[p];
}
}
}
inline void calc(){
for(int i=1;i<=cnt;i++) c[len[i]]++;
for(int i=1;i<=cnt;i++) c[i]+=c[i-1];
for(int i=cnt;i>=1;i--) a[c[len[i]]--]=i;
for(int i=cnt;i>=1;i--) size[fa[a[i]]]+=size[a[i]];
for(int i=1;i<=cnt;i++){
int q=a[i];d[q]=d[fa[q]]+1;f[q][0]=fa[q];
for(int j=1;(1<<j)<=d[q];j++){
if(!f[q][j-1]) break;
else f[q][j]=f[f[q][j-1]][j-1];
}
}
}
inline void query(int x,int y){
int p=pos[y];
for(int i=19;i>=0;i--){
if(len[f[p][i]]>=y-x+1) p=f[p][i];
}ans=max(ans,(ll)size[p]*(y-x+1));
}
inline void manacher(){
int maxright=0,mid=0;
str[0]='+';str[n+1]='-';
for(int i=1;i<=n;i++){
if(i<maxright) mx[i]=min(mx[2*mid-i-1],maxright-i);
else mx[i]=0;
while(str[i+mx[i]+1]==str[i-mx[i]]) mx[i]++,query(i-mx[i]+1,i+mx[i]);
if(maxright<i+mx[i]) mid=i,maxright=i+mx[i];
}maxright=0;
for(int i=1;i<=n;i++){
if(i<maxright) mx[i]=min(mx[2*mid-i],maxright-i-1);
else mx[i]=1,query(i-mx[i]+1,i+mx[i]-1);
while(str[i+mx[i]]==str[i-mx[i]]) mx[i]++,query(i-mx[i]+1,i+mx[i]-1);
if(maxright<i+mx[i]) mid=i,maxright=i+mx[i];
}
}
int main(){
scanf("%s",str+1);n=strlen(str+1);last=root=++cnt;
for(int i=1;i<=n;i++) ins(str[i]-'a',i);
calc();manacher();
printf("%lld\n",ans);
return 0;
}
方法二:这个方法就比较大神了
来自于2015年张天扬集训队论文
先按照原串建立SAM,然后用反串在上面跑匹配,如果这一段能匹配上,并且覆盖了当前节点的最右边的一个数maxpos(这个在做完SAM以后要跟着size一起更新),那么说明
[l,pos]
[
l
,
p
o
s
]
这一段一定是回文,因此找它的个数就可以了
还是很难理解啊…还是看代码吧…
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 300010
#define ll long long
char str[N];ll ans=0;
int root,last,cnt=0,n,size[N<<1],fa[N<<1],son[N<<1][26],c[N<<1],a[N<<1],mx[N<<1],pos[N<<1],vis[N<<1];
inline void ins(int ch,int id){
int p=last,np=++cnt;last=np;mx[np]=mx[p]+1;size[np]++;pos[np]=id;
while(p && !son[p][ch]) son[p][ch]=np,p=fa[p];
if(!p) fa[np]=root;
else{
int q=son[p][ch];
if(mx[q]==mx[p]+1) fa[np]=q;
else{
int nq=++cnt;mx[nq]=mx[p]+1;pos[nq]=id;
memcpy(son[nq],son[q],sizeof(son[q]));
fa[nq]=fa[q];fa[q]=fa[np]=nq;
while(son[p][ch]==q) son[p][ch]=nq,p=fa[p];
}
}
}
int main(){
scanf("%s",str+1);n=strlen(str+1);last=root=++cnt;
for(int i=1;i<=n;i++) ins(str[i]-'a',i);
for(int i=1;i<=cnt;i++) c[mx[i]]++;
for(int i=1;i<=cnt;i++) c[i]+=c[i-1];
for(int i=cnt;i>=1;i--) a[c[mx[i]]--]=i;
for(int i=cnt;i>=1;i--) size[fa[a[i]]]+=size[a[i]],pos[fa[a[i]]]=max(pos[fa[a[i]]],pos[a[i]]);
int p=root,l=0;
for(int i=n;i>=1;i--){
int ch=str[i]-'a';
while(p && !son[p][ch]) p=fa[p],l=mx[p];
if(son[p][ch]) p=son[p][ch],l++;
if(i+l>pos[p]){
if(i<pos[p]) ans=max(ans,(ll)size[p]*(pos[p]-i+1));
for(int q=p;q && !vis[q];q=fa[q]){
vis[q]=true;
if(i<=pos[q] && pos[q]<=i+mx[q]-1) ans=max(ans,(ll)size[q]*(pos[q]-i+1));
}
}
} printf("%lld\n",ans);
return 0;
}