后缀数组SA
模板
花了不少时间才理解倍增求 S A SA SA的实现方法,我还是太菜了。
定义
s
a
[
i
]
sa[i]
sa[i]表示排名为
i
i
i的后缀的起始位置。
定义
r
a
n
k
[
i
]
rank[i]
rank[i]表示起始位置为
i
i
i的后缀的排名。
显然两者之前互逆。
void solve()
{
int m=122;
for (int i=1;i<=m;i++) cnt[i]=0;
for (int i=1;i<=n;i++) cnt[x[i]=a[i]]++;
for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for (int i=n;i>=1;i--) sa[cnt[x[i]]--]=i;
for (int k=1;k<=n;k<<=1)
{
int p=0;
for (int i=1;i<=m;i++) y[i]=0;
for (int i=n-k+1;i<=n;i++) y[++p]=i;
for (int i=1;i<=n;i++) if (sa[i]>k) y[++p]=sa[i]-k;
for (int i=0;i<=m;i++) cnt[i]=0;
for (int i=1;i<=n;i++) cnt[x[y[i]]]++;
for (int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for (int i=n;i>=1;i--) sa[cnt[x[y[i]]]--]=y[i];
swap(x,y);
x[sa[1]]=p=1;
for (int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
if (p>=n) break;
m=p;
}
}
Height以及LCP
定义 L C P ( x , y ) LCP(x,y) LCP(x,y)表示字符串 x x x与 y y y之间的最长公共前缀长度。
定义 h e i g h t [ i ] height[i] height[i]表示 s u f f i x [ s a [ i − 1 ] ] suffix[sa[i-1]] suffix[sa[i−1]]和 s u f f i x [ s a [ i ] ] suffix[sa[i]] suffix[sa[i]]的 L C P LCP LCP的长度,即相邻排名的后缀的 L C P LCP LCP长度。
容易发现一个性质
对于任意的
j
,
k
j,k
j,k,若
r
a
n
k
[
j
[
<
r
a
n
k
[
k
]
rank[j[<rank[k]
rank[j[<rank[k],则
s
u
f
f
i
x
(
j
)
,
s
u
f
f
i
x
(
k
)
suffix(j),suffix(k)
suffix(j),suffix(k)的
L
C
P
LCP
LCP的长度为
m
i
n
i
=
r
a
n
k
[
j
]
+
1
r
a
n
k
[
k
]
h
e
i
g
h
t
[
i
]
min_{i=rank[j]+1}^{rank[k]}height[i]
mini=rank[j]+1rank[k]height[i]
即两个后缀
j
,
k
j,k
j,k的
L
C
P
LCP
LCP长度是排名在它们之间所有的后缀(包括
s
u
f
f
i
x
(
k
)
suffix(k)
suffix(k))的
h
e
i
g
h
t
height
height值的最小值。
根据这一性质,倘若我们可以求出 h e i g h t [ i ] height[i] height[i],我们就可以通过 S T ST ST表, O ( n l g n ) O(nlgn) O(nlgn)预处理, O ( 1 ) O(1) O(1)询问两个后缀的 L C P LCP LCP答案了。
而对于
h
e
i
g
h
t
height
height数组,也有一个重要的性质:
h
e
i
g
h
t
[
i
−
1
]
−
1
≤
h
e
i
g
h
t
[
i
]
height[i-1]-1\leq height[i]
height[i−1]−1≤height[i]
于是可以
O
(
n
)
O(n)
O(n)计算
h
e
i
g
h
t
height
height了。
void get_height()
{
for (int i=1;i<=n;i++) rnk[sa[i]]=i;
for (int i=1,j=0;i<=n;i++)
{
if (j) j--;
while (a[i+j]==a[sa[rnk[i]-1]+j]) j++;
height[rnk[i]]=j;
}
}
SA的简单应用
1.求最长重复子串
答案为 h e i g h t height height最大值。
2.求最长重复k次子串
二分答案,转化为判断是否存在长度为 x x x的子串重复至少 k k k次。将所有相邻且 h e i g h t height height大于等于 x x x的都分在一组,判断是否有一组的后缀个数大于 k k k即可。
时间复杂度 O ( n ) O(n) O(n)
3.本质不同子串个数
答案为产生的新串数量-重复出现的串的数量。
则
∑
i
(
n
−
i
+
1
)
−
h
e
i
g
h
t
[
r
n
k
[
i
]
]
\sum_{i} (n-i+1)-height[rnk[i]]
∑i(n−i+1)−height[rnk[i]]。