回文串 BZOJ 3676
回文串
【问题描述】
考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最大出现值。
【输入格式】
输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。
【输出格式】
输出一个整数,为逝查回文子串的最大出现值。
【样例输入1】
abacaba
【样例输入2】
www
【样例输出1】
7
【样例输出2】
4
题解:
我们先用 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 }