【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
",ans);
86     return 0;
87 }

END

原文地址:https://www.cnblogs.com/antiquality/p/10526075.html