后缀数组??!!
后缀数组是处理字符串的强有力的工具…..
在字符串处理当中,后缀树和后缀数组都是非常有力的工具。其实后缀数组是后缀树的一个非常精巧的替代品,它比后缀树容易编程实现,能够实现后缀树的很多功能而时间复杂度也不太逊色,并且,它比后缀树所占用的空间小很多。可以说,在信息学竞赛中后缀数组比后缀树要更为实用。
−−−−
−
−
−
−
百度百科
后缀数组中变量的定义:
我们定义
suffix(i)
s
u
f
f
i
x
(
i
)
为以i开始到结束的后缀.
SA[i]
S
A
[
i
]
:排第
i
i
名的后缀的位置,排名是按照字典序来排的
:
suffix(i)
s
u
f
f
i
x
(
i
)
排第几
这时候我们就会发现
SA[]和Rank[]
S
A
[
]
和
R
a
n
k
[
]
互为逆运算,即
SA=Rank−1
S
A
=
R
a
n
k
−
1
c[]
c
[
]
:基数排序的数组
x[],y[]
x
[
]
,
y
[
]
:第一二关键字的排名
我们怎样构建后缀数组呢?
我们就是对所有的
suffix(i)
s
u
f
f
i
x
(
i
)
进行排序,然后计算出
SA[]和Rank[]
S
A
[
]
和
R
a
n
k
[
]
要排序的话快排是
O(nlogn)
O
(
n
l
o
g
n
)
,我们可以用基数排序来
O(n)
O
(
n
)
的时间来进行排序
后缀数组的构建
一般两种方法
D3
D
3
和倍增
倍增相对来说比较好写,所以我们这里介绍倍增算法
我们就是先选定当前
i
i
的排名作为第一关键字,然后往后第个字符的排名作为第二个关键字,直到
2k>n
2
k
>
n
后缀数组的奥秘就隐藏在下面这一张图中
假如我们现在要构建
aabab
a
a
b
a
b
的后缀数组
下面是倍增求排名的过程
下面是
SA和Rank
S
A
和
R
a
n
k
数组
代码
后缀数组的构建难理解的就是基数排序;
void Build_SA()
{
int n=Len,m=150;
for (int i=0;i<m;i++) c[i]=0;
for (int i=0;i<n;i++) c[x[i]=s[i]]++;
for (int i=1;i<m;i++) c[i]+=c[i-1];
for (int i=n-1;i>=0;i--) SA[--c[x[i]]]=i;
//先处理出单个字符的排名
for (int k=1;k<=n;k<<=1)//倍增搞一搞
{
int p=0;
for (int i=n-k;i<n;i++) y[p++]=i;//第二关键字的排序
for (int i=0;i<n;i++) if (SA[i]>=k) y[p++]=SA[i]-k;
for (int i=0;i<m;i++) c[i]=0;
for (int i=0;i<n;i++) c[x[i]]++;
for (int i=1;i<m;i++) c[i]+=c[i-1];
for (int i=n-1;i>=0;i--)SA[--c[x[y[i]]]]=y[i];//第一二关键字一起排
swap(x,y);
p=1; x[SA[0]]=0;
for (int i=1;i<n;i++)
x[SA[i]]=y[SA[i-1]]==y[SA[i]]&&((SA[i-1]+k>=n?-1:y[SA[i-1]+k])==(SA[i]+k>=n?-1:y[SA[i]+k]))?p-1:p++;//下一次排序继续用,更新SA[]
if (p>n) break;
m=p;
}
}
Height数组
后缀数组比较有名还源于
Height
H
e
i
g
h
t
数组
Height
H
e
i
g
h
t
数组的求解可以做到
O(n)
O
(
n
)
这要基于
Height[]
H
e
i
g
h
t
[
]
的一个性质
Height[i]>=Height[i−1]−1
H
e
i
g
h
t
[
i
]
>=
H
e
i
g
h
t
[
i
−
1
]
−
1
这里的
i
i
<script type="math/tex" id="MathJax-Element-1543">i</script>指的是排名
void Built_Height()
{
for (int i=0;i<n;++i) Rank[SA[i]]=i;//利用逆运算的性质求出Rank
int k=0;Height[0]=0;
for (int i=0;i<n;++i)
{
if (!Rank[i]) continue;
if (k) --k;
int j=SA[Rank[i]-1];
while (i+k<n&&j+k<n&&s[i+k]==s[j+k])
++k;//根据性质来求
Height[Rank[i]]=k;
}
}