回文串 BZOJ 3676

回文串

【问题描述】

考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最大出现值。 

【输入格式】

输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。 

【输出格式】

输出一个整数,为逝查回文子串的最大出现值。 

【样例输入1】

abacaba

【样例输入2】

www

【样例输出1】

7

【样例输出2】


题解:

我们先用 Manacher 找出所有本质不同的回文,即 Manacher 的每一次拓展出的回文

对于每一个回文,我们已经知道了它的区间,即知道了长度与右区间,那么就能在 Sam 上确定它所属的 Right 的集合

为了确定回文 [ l , r ] 所在的状态,我们记录所有有 1 - i 的字符组成的字符串所在的状态,记为 pos[i]

那么每次查找时,从 pos[r] 开始在 Sam 的 fail 树上倍增,就能确定回文所在的状态,那么这个状态 Right 集合的大小就是回文出现的次数

更新答案即可

  1 #include<cmath>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<iostream>
  6 #include<algorithm>
  7 using namespace std;
  8 const int lgn = 18;
  9 const int maxn = 3e5 + 233;
 10 const int maxp = 6e5 + 233; 
 11 int n;
 12 long long ans;
 13 char s[maxn];
 14 int r[maxp];
 15 char sx[maxp];
 16 int fat[lgn + 1][maxp];
 17 struct one
 18 {
 19     int num, last;
 20     int w[maxn], si[maxp], que[maxp];
 21     int pos[maxn], maxr[maxp], fail[maxp], tran[maxp][26];
 22     inline one()
 23     {
 24         num = last = 1;
 25     }
 26     inline void ins(int i)
 27     {
 28         int anc = last, asc = s[i - 1] - 'a', now = last = ++num;
 29         pos[i] = now;
 30         si[now] = 1;
 31         maxr[now] = maxr[anc] + 1;
 32         while(anc && !tran[anc][asc]) tran[anc][asc] = now, anc = fail[anc];
 33         if(!anc) fail[now] = 1;
 34         else
 35         {
 36             int son = tran[anc][asc];
 37             if(maxr[son] == maxr[anc] + 1) fail[now] = son;
 38             else
 39             {
 40                 int rep = ++num;
 41                 maxr[rep] = maxr[anc] + 1;
 42                 memcpy(tran[rep], tran[son], sizeof(tran[rep]));
 43                 fail[rep] = fail[son];
 44                 fail[son] = fail[now] = rep;
 45                 while(tran[anc][asc] == son) tran[anc][asc] = rep, anc = fail[anc];
 46             }
 47         }
 48     }
 49     inline void size()
 50     {
 51         for(int i = 1; i <= num; ++i) ++w[maxr[i]];
 52         for(int i = 1; i <= n; ++i) w[i] += w[i - 1];
 53         for(int i = num; i >= 1; --i) que[w[maxr[i]]--] = i;
 54         for(int i = num; i >= 1; --i) si[fail[que[i]]] += si[que[i]];
 55     }
 56     inline void rmq()
 57     {
 58         for(int i = 1; i <= num; ++i) fat[0][i] = fail[i];
 59         for(int k = 1; k <= lgn; ++k)
 60             for(int i = 1; i <= num; ++i)
 61                 fat[k][i] = fat[k - 1][fat[k - 1][i]];
 62     }
 63 };
 64 one sam;
 65 inline void Sam()
 66 {
 67     for(int i = 0; i < n; ++i) sam.ins(i + 1);
 68     sam.size();
 69     sam.rmq();
 70 }
 71 inline void Solve(int l, int r)
 72 {
 73     int x = sam.pos[r], len = r - l + 1;
 74     for(int i = lgn; i >= 0; --i)
 75         if(sam.maxr[fat[i][x]] >= len) x = fat[i][x];
 76     ans = max(ans, (long long) sam.si[x] * len);
 77 }
 78 inline void Manacher()
 79 {
 80     int m = 0;
 81     sx[0] = '*';
 82     for(int i = 0; i < n; ++i) sx[++m] = s[i], sx[++m] = '#';
 83     sx[m] = '+';
 84     int id = 0, mx = 0;
 85     for(int i = 1; i < m; ++i)
 86     {
 87         if(mx > i) r[i] = min(r[(id << 1) - i], mx - i + 1);
 88         while(sx[i + r[i]] == sx[i - r[i]])
 89         {
 90             if(sx[i + r[i]] != '#') Solve((i - r[i] >> 1) + 1, (i + r[i] >> 1) + 1);
 91             else Solve((i - r[i] + 1 >> 1) + 1, (i + r[i] - 1 >> 1) + 1);
 92             ++r[i];
 93         }
 94         if(i + r[i] - 1 > mx) mx = i + r[i] - 1, id = i;
 95     }
 96 }
 97 int main()
 98 {
 99     scanf("%s", s);
100     n = strlen(s);
101     Sam();
102     Manacher();
103     printf("%lld", ans);
104 }
posted @ 2017-04-22 17:29  草根柴鸡  阅读(143)  评论(0编辑  收藏  举报