bzoj 3676 [Apio2014]回文串(Manacher+SAM)

【题目链接】

http://www.lydsy.com/JudgeOnline/problem.php?id=3676

【题意】

给定一个字符串,定义一个串的权值为长度*出现次数,求最大权的回文子串。

【思路】

马拉车求出本质不同的回文子串。

对于一个回文子串,在SAM中用倍增法在O(logn)的时间得到它的出现次数,即SAM中每个节点的right集大小,倍增数组和right都可以通过提前处理得到。

更新答案即可。

【代码】

  1 #include<set>
  2 #include<cmath>
  3 #include<queue>
  4 #include<vector>
  5 #include<cstdio>
  6 #include<cstring>
  7 #include<iostream>
  8 #include<algorithm>
  9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
 11 #define rep(a,b,c) for(int a=(b);a>=(c);a--)
 12 using namespace std;
 13
 14 typedef long long ll;
 15 const int N = 6e5+10;
 16 const int D = 21;
 17
 18 char s[N];
 19 int n,p[N];
 20
 21 struct SAM
 22 {
 23
 24     ll ans;
 25     int sz,last,ch[N][26],fa[N],R[N],pos[N],l[N],b[N],cnt[N],fat[N][D];
 26     SAM()
 27     {
 28         sz=ans=0; last=++sz;
 29         memset(cnt,0,sizeof(cnt));
 30         memset(R,0,sizeof(R));
 31         memset(fat,0,sizeof(fat));
 32     }
 33     void add(int c,int id)
 34     {
 35         int np=++sz,p=last; last=np;
 36         l[np]=l[p]+1; R[np]=1; pos[id]=last;
 37         for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
 38         if(!p) fa[np]=1;
 39         else {
 40             int q=ch[p][c];
 41             if(l[q]==l[p]+1) fa[np]=q;
 42             else {
 43                 int nq=++sz; l[nq]=l[p]+1;
 44                 memcpy(ch[nq],ch[q],sizeof(ch[q]));
 45                 fa[nq]=fa[q];
 46                 fa[q]=fa[np]=nq;
 47                 for(;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
 48             }
 49         }
 50     }
 51     void get_pre()
 52     {
 53         FOR(i,1,sz) cnt[l[i]]++;
 54         FOR(i,1,n) cnt[i]+=cnt[i-1];
 55         rep(i,sz,1) b[cnt[l[i]]--]=i;
 56         rep(i,sz,1) R[fa[b[i]]]+=R[b[i]];
 57
 58         FOR(i,1,sz) {
 59             fat[i][0]=fa[i];
 60             FOR(j,1,D-1)
 61                 fat[i][j]=fat[fat[i][j-1]][j-1];
 62         }
 63     }
 64     void get_ans(int u,int v)
 65     {
 66         int x=pos[v];
 67         for(int i=D-1;i>=0;i--) {
 68             int t=fat[x][i];
 69             if(l[t]>=v-u+1) x=t;
 70         }
 71         ans=max(ans,(ll)R[x]*(v-u+1));
 72     }
 73
 74 } sam;
 75
 76 void Manacher()
 77 {
 78     int mx=0,id;
 79     for(int i=1;i<=n;i++) {
 80         if(mx>i) p[i]=min(mx-i,p[2*id-i-1]);
 81         else p[i]=0;
 82         while(s[i+p[i]+1]==s[i-p[i]]) {
 83             p[i]++;
 84             sam.get_ans(i-p[i]+1,i+p[i]);
 85         }
 86         if(p[i]+i>mx) mx=p[i]+i,id=i;
 87     }
 88     mx=0;
 89     for(int i=1;i<=n;i++) {
 90         if(mx>i) p[i]=min(mx-i-1,p[2*id-i]);
 91         else p[i]=1,sam.get_ans(i,i);
 92         while(s[i+p[i]]==s[i-p[i]]) {
 93             p[i]++;
 94             sam.get_ans(i-p[i]+1,i+p[i]-1);
 95         }
 96         if(p[i]+i>mx) mx=p[i]+i,id=i;
 97     }
 98 }
 99
100 int main()
101 {
102     scanf("%s",s+1);
103     n=strlen(s+1);
104     FOR(i,1,n) sam.add(s[i]-‘a‘,i);
105     sam.get_pre();
106     s[0]=‘+‘,s[n+1]=‘-‘;
107     Manacher();
108     printf("%lld",sam.ans);
109     return 0;
110 }
时间: 2024-10-10 07:10:51

bzoj 3676 [Apio2014]回文串(Manacher+SAM)的相关文章

BZOJ 3676: [Apio2014]回文串 回文串自动机

裸的回文串自动机 3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 504  Solved: 152 [Submit][Status][Discuss] Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的"出 现值"为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行,为一个只包含小写字母(a -z)的非空字符串s

bzoj 3676: [Apio2014]回文串 回文自动机

3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 844  Solved: 331[Submit][Status][Discuss] Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行,为一个只包含小写字母(a -z)的非空字符串s. Output 输出一个整数,

[BZOJ3676][APIO2014]回文串(Manacher+SAM)

代码总用时:3h 很简单的一道题,只要意识到Manacher算法的本质(本质不同的回文串的个数是O(n)的),配合后缀自动机或者后缀数组就可以轻松解决. 但这道题调了好久,浪费了很多时间,一是因为后缀自动机模板不熟练,而是Manacher算法流程没有一个清楚的认识. 写代码的时候精力要高度集中,不能因为低级错误耽误时间. 下面是SAM版本的代码: #include<cstdio> #include<cstring> #include<algorithm> #define

字符串(马拉车算法,后缀数组,稀疏表):BZOJ 3676 [Apio2014]回文串

Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行,为一个只包含小写字母(a -z)的非空字符串s. Output 输出一个整数,为逝查回文子串的最大出现值. Sample Input [样例输入l] abacaba [样例输入2] www Sample Output [样例输出l] 7 [样例输出2] 4 HINT 一个串是回文的,当且仅当它从左

BZOJ 3676 [Apio2014]回文串(回文树)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3676 [题目大意] 考虑一个只包含小写拉丁字母的字符串s. 我们定义s的一个子串t的"出现值"为t在s中的出现次数乘以t的长度. 求s的所有回文子串中的最大出现值. [题解] 我们对给出串建立回文树,统计每个回文串出现次数和长度,相乘取组大即可 [代码] #include <cstdio> #include <algorithm> #include

bzoj 3676: [Apio2014]回文串

Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的"出 现值"为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行,为一个只包含小写字母(a -z)的非空字符串s. Output 输出一个整数,为逝查回文子串的最大出现值. Sample Input [样例输入l] abacaba [样例输入2] www Sample Output [样例输出l] 7 [样例输出2] 4 HINT 一个串是回文的,当且

BZOJ 3676: [Apio2014]回文串 后缀自动机 Manacher 倍增

http://www.lydsy.com/JudgeOnline/problem.php?id=3676 过程很艰难了,第一次提交Manacher忘了更新p数组,超时,第二次是倍增的第0维直接在自动机里完成,但是忽略了增加新点时fa变动的情况,还是肉眼查错最管用. 得到的教训是既然倍增就在倍增的函数里完成,自动机就在自动机里完成,不要随便乱搞赋值. 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm>

【BZOJ】3676 [Apio2014]回文串

[算法]回文树 [题解]建回文数,然后一个回文子串出现的次数就是结点被访问的次数以及能包含它的结点被访问的次数. 根据fail树反向建新树,那么答案就是结点所在子树的权值和(权值就是结点被访问次数). #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=300010; char s[maxn]; int n,len,l,sz,first[ma

【BZOJ 3676】 [Apio2014]回文串

3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MB Submit: 646 Solved: 219 [Submit][Status][Discuss] Description 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的"出 现值"为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. Input 输入只有一行,为一个只包含小写字母(a -z)的非空字符串s. Output 输出