【SAM manacher 倍增】bzoj3676: [Apio2014]回文串
做法一:PAM;做法二:SAM+manacher.前缀树上倍增
Description
考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最
大出现值。
Input
输入只有一行,为一个只包含小写字母(a -z)的非空字符串s。
Output
输出一个整数,为逝查回文子串的最大出现值。
【数据规模与评分】
数据满足1≤字符串长度≤300000。
题目分析
考虑暴力怎么做:首先对字符串$S$建立SAM,再使用manacher对所有$O(n)$级别的本质不同的回文子串查询出现个数。由于每次查询是$O(n)$的,所以暴力的复杂度是$O(n^2)$.
有一种SAM上计数的基本套路:在前缀树上倍增或是其他在树上的常规操作。对于这题则是考虑用倍增快速查询$S(l,r)$在全串中的出现次数。
用$fa[i][j]$表示点$i$在前缀树上向上跳$2^j$步的父亲点数,那么从$p=id[r]$(即子串$S(1,r)$插入完毕的一部分SAM)开始查询,若$len[p]≥r-l+1$就说明点$p$所在的子树仍都是$S(l,r)$的后缀,因此一直向上跳直到不满足限制的点$p$的$size[p]$就是回文子串$S(l,r)$出现次数。
1 #include<bits/stdc++.h> 2 const int maxn = 600035; 3 4 int n,g[maxn],lg2[maxn>>1]; 5 long long ans; 6 int size[maxn],dep[maxn],id[maxn],sta[maxn]; 7 int cnt[maxn],pos[maxn]; 8 char s[maxn],t[maxn]; 9 struct SAM 10 { 11 int lst,tot; 12 int fa[maxn][21],len[maxn]; 13 std::map<int, int> ch[maxn]; //似乎略卡空间 14 SAM(){lst = tot = 1;} 15 void extend(int c, int id) 16 { 17 int p = lst, np = ++tot; 18 lst = np, len[np] = len[p]+1, sta[id] = np; //sta[np]=id 19 for (; p&&!ch[p][c]; p=fa[p][0]) ch[p][c] = np; 20 if (!p) fa[np][0] = 1; 21 else{ 22 int q = ch[p][c]; 23 if (len[q]==len[p]+1) fa[np][0] = q; 24 else{ 25 int nq = ++tot; 26 len[nq] = len[p]+1, ch[nq] = ch[q]; 27 fa[nq][0] = fa[q][0], fa[q][0] = fa[np][0] = nq; 28 for (; ch[p][c]==q; p=fa[p][0]) ch[p][c] = nq; 29 } 30 } 31 size[np] = 1; 32 } 33 void build() 34 { 35 for (int i=1; i<=tot; i++) ++cnt[len[i]]; 36 for (int i=1; i<=tot; i++) cnt[i] += cnt[i-1]; 37 for (int i=tot; i>=1; i--) pos[cnt[len[i]]--] = i; 38 for (int i=tot; i>=1; i--) 39 size[fa[pos[i]][0]] += size[pos[i]]; 40 for (int i=1; i<=tot; i++) 41 { 42 int p = pos[i]; 43 dep[p] = dep[fa[p][0]]+1; 44 for (int j=1; j<=20; j++) 45 fa[i][j] = fa[fa[i][j-1]][j-1]; 46 } 47 } 48 void match(int l, int r) 49 { 50 if (l > r||l < 1||r > n) return; 51 int p = sta[r]; 52 for (int i=lg2[dep[p]]; i>=0; i--) 53 { 54 int fat = fa[p][i]; 55 if (len[fat] >= r-l+1) p = fat; 56 } 57 ans = std::max(ans, 1ll*size[p]*(r-l+1)); 58 } 59 }f; 60 61 void manacher() 62 { 63 int m = 0, mid = 1; 64 t[0] = '!', t[++m] = '@'; 65 for (int i=1; i<=n; i++) 66 t[++m] = s[i], id[m] = i, t[++m] = '@'; 67 for (int i=1, mx=-1; i<m; i++) 68 { 69 if (i >= mx) g[i] = 1; 70 else g[i] = std::min(mx-i, g[2*mid-i]); 71 f.match(id[i-g[i]+2], id[i+g[i]-2]); 72 for (; t[i-g[i]]==t[i+g[i]]; ) 73 ++g[i], f.match(id[i-g[i]+2], id[i+g[i]-2]); //id[i-g[i]], id[i+g[i]] 74 if (i+g[i] > mx) mx = i+g[i], mid = i; 75 } 76 } 77 int main() 78 { 79 scanf("%s",s+1); 80 n = strlen(s+1); 81 for (int i=1; i<=n; i++) 82 f.extend(s[i]-'a', i), lg2[i] = i>1?(lg2[i>>1]+1):0; 83 f.build(); 84 manacher(); 85 printf("%lld\n",ans); 86 return 0; 87 }
END