[manacher 后缀自动机 || 回文自动机] BZOJ 3676 [Apio2014]回文串

本质不同的回文串只有 O(n) 个 就是做manacher时变长的时候
那么直接在SAM上找找出现几次就好了
回文自动机?我不会啊

#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(char *s){
  char c=nc(); int len=0;
  for (;!(c>='a' && c<='z');c=nc());
  for (;c>='a' && c<='z';s[++len]=c,c=nc()); s[++len]=0; return len-1;
}

const int N=600005;

struct state{
  int len,link,next[26];
  //map<int,int> next;
}st[N];
int last,ncnt;
int cnt[N];

inline void Extend(int c){
  int cur=++ncnt,p;
  st[cur].len=st[last].len+1; cnt[cur]=1;
  for (p=last;p && !st[p].next[c];p=st[p].link)
    st[p].next[c]=cur;
  if (!p)
    st[cur].link=1;
  else{
    int q=st[p].next[c];
    if (st[q].len==st[p].len+1)
      st[cur].link=q;
    else{
      int nq=++ncnt;
      st[nq].len=st[p].len+1;
      st[nq].link=st[q].link;
      for (int i=0;i<26;i++) st[nq].next[i]=st[q].next[i];
      //st[nq].next=st[q].next;
      for (;p && st[p].next[c]==q;p=st[p].link)
    st[p].next[c]=nq;
      st[q].link=st[cur].link=nq;
    }
  }
  last=cur;
}
int tmp[N],c[N];
inline void Sort(){
  for (int i=1;i<=ncnt;i++) c[st[i].len]++;
  for (int i=1;i<=ncnt;i++) c[i]+=c[i-1];
  for (int i=ncnt;i;i--) tmp[c[st[i].len]--]=i;
  for (int i=ncnt;i;i--)
    cnt[st[tmp[i]].link]+=cnt[tmp[i]];
}
const int K=19;
int fat[N][K];
inline void Pre(){
  for (int i=1;i<=ncnt;i++) fat[i][0]=st[i].link;
  for (int k=1;k<K;k++)
    for (int i=1;i<=ncnt;i++)
      fat[i][k]=fat[fat[i][k-1]][k-1];
}
int pos[N];
int nnn=0;
inline int query(int l,int r){
  l=(l+1)/2; r=(r+1)/2;
  //++nnn;
  int x=pos[r];
  for (int k=K-1;~k;k--)
    if (st[fat[x][k]].len>=r-l+1)
      x=fat[x][k];
  return cnt[x];
}

char s[N];
int n,a[N<<1];
int f[N<<1];
ll ans;

int main(){
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  last=ncnt=1; st[1].link=0;
  n=read(s);
  for (int i=1;i<=n;i++)
    a[2*i-1]=s[i]-'a',a[2*i]=26,Extend(s[i]-'a'),pos[i]=last;
  n=2*n-1; a[n+1]=-1; a[0]=-2;
  Sort(); Pre();
  int id=1,x=1; f[1]=1;
  x=query(1,1);
  ans=max(ans,(ll)x*f[1]);
  for (int i=2;i<=n;i++){
    if (i<=id+f[id]-1) f[i]=min(f[2*id-i],id+f[id]-i);
    else{
      f[i]=1;
      if (((i&1) && (f[i]&1)) || ((~i&1) && (~f[i]&1)))
    ans=max(ans,(ll)query(i,i)*f[i]);
    }
    while (a[i-f[i]]==a[i+f[i]]){
      f[i]++;
      if (((i&1) && (f[i]&1)) || ((~i&1) && (~f[i]&1)))
    ans=max(ans,(ll)query(i-f[i]+1,i+f[i]-1)*f[i]);
    }
    if (i+f[i]>id+f[id]) id=i;
  }
  printf("%lld\n",ans);
  //printf("%d\n",nnn);
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值