BZOJ 3676 [Apio2014]回文串 (后缀自动机+manacher/回文自动机)

题目大意:

给你一个字符串,求其中回文子串的长度*出现次数的最大值

明明是PAM裸题我干嘛要用SAM做

回文子串有一个神奇的性质,一个字符串本质不同的回文子串个数是$O(n)$级别的

用$manacher$的思想分析一下,$maxright$指针向右扩展才会产生新的回文串

其它的回文串都根据之前求得的信息得到的,比如根据回文中心对称,或者是不超过当前最长回文上限

每次扩展成功时,都把这个回文串放到$SAM$里跑

预处理出每个前缀结尾在$SAM$里的节点编号,就不用每次都从头跑了

沿着$pre$指针倍增地往上跳,直到跳到当前节点能表示出这个回文串为止

注意长度为1的串的情况

如果被卡空间了,会发现预处理以后,trs指针没什么用了,把它当成倍增的数组吧

  1 #include <cmath>
  2 #include <vector>
  3 #include <cstdio>
  4 #include <cstring>
  5 #include <algorithm>
  6 #define N1 305000
  7 #define S1 (N1<<1)
  8 #define T1 (N1<<2)
  9 #define ll long long
 10 #define uint unsigned int
 11 #define rint register int 
 12 #define dd double
 13 #define il inline 
 14 #define inf 0x3f3f3f3f
 15 #define idx(X) (X-'a')
 16 using namespace std;
 17 
 18 int gint()
 19 {
 20     int ret=0,fh=1;char c=getchar();
 21     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
 22     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
 23     return ret*fh;
 24 }
 25 int n,m,len;
 26 /*struct Edge{
 27 int head[S1],to[S1],nxt[S1],cte;
 28 void ae(int u,int v){
 29     cte++;to[cte]=v,nxt[cte]=head[u],head[u]=cte;}
 30 }E;*/
 31 namespace SAM{
 32 int trs[S1][26],pre[S1],dep[S1],ed[S1],pos[S1],la,tot;
 33 void init(){tot=la=1;}
 34 void insert(int c,int id)
 35 {
 36     int p=la,np=++tot,q,nq;la=np;
 37     dep[np]=dep[p]+1;
 38     pos[id]=np,ed[np]=1;
 39     for(;p&&!trs[p][c];p=pre[p]) trs[p][c]=np;
 40     if(!p) {pre[np]=1;return;}
 41     q=trs[p][c];
 42     if(dep[q]==dep[p]+1) pre[np]=q;
 43     else{
 44         pre[nq=++tot]=pre[q];
 45         pre[q]=pre[np]=nq;
 46         dep[nq]=dep[p]+1;
 47         memcpy(trs[nq],trs[q],sizeof(trs[q]));
 48         for(;p&&trs[p][c]==q;p=pre[p]) trs[p][c]=nq;
 49     }
 50 }
 51 int hs[S1],que[S1],sz[S1];
 52 //int ff[S1][20];
 53 void build()
 54 {
 55     int i,j,x;
 56     for(i=1;i<=tot;i++) hs[dep[i]]++;
 57     for(i=1;i<=n;i++) hs[i]+=hs[i-1];
 58     for(i=1;i<=tot;i++) que[hs[dep[i]]--]=i;
 59     for(i=tot;i;i--)
 60     {
 61         x=que[i];//E.ae(pre[x],x);
 62         if(ed[x]) sz[x]++;
 63         sz[pre[x]]+=sz[x];
 64         trs[x][0]=x,trs[x][1]=pre[x];
 65     }
 66     for(j=2;j<=20;j++)
 67         for(i=1;i<=tot;i++)
 68         trs[i][j]=trs[ trs[i][j-1] ][j-1];
 69 }
 70 int solve(int x,int s,int e)
 71 {
 72     int fx,L=e-s+1;
 73     for(int j=20;j>=0;j--)
 74     //for(int j=5;j>=0;j--)
 75     {
 76         if(!trs[x][j]) continue;
 77         fx=trs[x][j];
 78         if(dep[fx]>=L) x=fx;
 79     }
 80     return sz[x];
 81 }
 82 };
 83 char str[N1],man[S1];
 84 int p[S1],hs[30];
 85 
 86 int main()
 87 {
 88     //freopen("t1.in","r",stdin);
 89     scanf("%s",str+1);
 90     n=strlen(str+1);
 91     man[0]='$',man[1]='#';
 92     int i,j,mr=2,mid=1,l,r,x;
 93     SAM::init();
 94     for(i=1;i<=n;i++) SAM::insert(idx(str[i]),i);
 95     SAM::build();
 96     for(i=1;i<=n;i++) man[2*i]=str[i],man[2*i+1]='#';
 97     p[1]=1;
 98     ll ans=0;
 99     for(i=2;i<=2*n+1;i++)
100     {
101         if(i<mr) p[i]=min(p[2*mid-i],mr-i);
102         else p[i]=1;
103         while(man[i-p[i]]==man[i+p[i]])
104         {
105             if(!((i+p[i])&1))
106             {
107                 l=(i-p[i])>>1;
108                 r=(i+p[i])>>1;
109                 x=SAM::pos[r];
110                 ans=max(ans,1ll*(r-l+1)*SAM::solve(x,l,r));
111             }
112             p[i]++;
113         }
114         if(i+p[i]>mr) mr=i+p[i],mid=i;
115     }
116     for(i=1;i<=n;i++)
117         if(!hs[idx(str[i])])
118         {
119             hs[idx(str[i])]=1;
120             l=i,r=i,x=SAM::pos[r];
121             ans=max(ans,1ll*SAM::solve(x,l,r));
122         }
123     printf("%lld\n",ans);
124     return 0;
125 }

进入正题

这明明是一道$PAM$裸题嘛

$PAM$的构造方式类似于$AC$自动机..但它仍然有许多美妙的性质

比如奇回文树是树根节点深度设为-1等等...

而且$PAM$代码非常短

 1 #include <cmath>
 2 #include <vector>
 3 #include <cstdio>
 4 #include <cstring>
 5 #include <algorithm>
 6 #define N1 301000
 7 #define S1 (N1<<1)
 8 #define ll long long
 9 #define uint unsigned int
10 #define rint register int 
11 #define dd double
12 #define il inline 
13 #define inf 0x3f3f3f3f
14 #define idx(X) (X-'a')
15 using namespace std;
16 
17 int gint()
18 {
19     int ret=0,fh=1;char c=getchar();
20     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
21     while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();}
22     return ret*fh;
23 }
24 int n,len,cnt;
25 namespace PAM{
26 int trs[N1][26],pre[N1],dep[N1],sz[N1],la,tot;
27 void init(){la=tot=1,pre[0]=pre[1]=1,dep[1]=-1;}
28 int ntsym(char *str,int i,int p){return str[i-dep[p]-1]!=str[i]?1:0;}
29 void insert(char *str,int i)
30 {
31     int p=la,np,fp,c=idx(str[i]);
32     while(ntsym(str,i,p)) p=pre[p];
33     if(!trs[p][c])
34     {
35         np=++tot;
36         dep[np]=dep[p]+2;
37         fp=pre[p];
38         while(ntsym(str,i,fp)) fp=pre[fp];
39         pre[np]=trs[fp][c];
40         trs[p][c]=np;
41     }
42     la=trs[p][c];
43     sz[trs[p][c]]++;
44     return;
45 }
46 ll topo()
47 {
48     ll ans=0;
49     for(int x=tot;x>1;x--)
50         sz[pre[x]]+=sz[x],ans=max(ans,1ll*dep[x]*sz[x]);
51     return ans;
52 }
53 };
54 char str[N1];
55 
56 int main()
57 {
58     //freopen("t2.in","r",stdin);
59     //freopen("a.out","w",stdout);
60     int i;PAM::init();
61     scanf("%s",str+1);len=strlen(str+1);
62     for(i=1;i<=len;i++) PAM::insert(str,i);
63     printf("%lld\n",PAM::topo());
64     return 0;
65 }

 

posted @ 2018-12-14 11:59  guapisolo  阅读(215)  评论(0编辑  收藏  举报