前言
刚刚学完回文自动机
来学后缀数组
一开始思路看得懂
但是代码看不懂呀……
一堆神仙代码
引入
思路
勿谓我,何强过者,炸哉!
我们需要一种新的算法——后缀数组
首先,输入字符串
scanf("%s", ch+1);
n=strlen(ch+1);
然后,按照题意
Suffix_Sort(ch);
for(int i=1; i<=n; i++) printf("%d ",sa[i]);
主程序就是这么简单
int main(){
scanf("%s", ch+1);
n=strlen(ch+1);
Suffix_Sort(ch);
for(int i=1; i<=n; i++) printf("%d ",sa[i]);
return 0;
}
好了,废话完了,步入正题
Suffix_Sort函数
inline void Suffix_Sort(char ch[]) {
for(int i=1; i<=n; i++) {
rak[i]=ch[i]-'0'+1;
tp[i]=i;
}
Sort();
for(int w=1, p=0; p<n; m=p, w<<=1) {
p=0;
for(int i=1; i<=w; i++) tp[++p]=n-w+i;
for(int i=1; i<=n; i++) if(sa[i]>w) tp[++p]=sa[i]-w;
Sort();memcpy(tp, rak, sizeof(rak));rak[sa[1]]=p=1;
for(int i=2; i<=n; i++)
rak[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&tp[sa[i-1]+w]==tp[sa[i]+w])? (p):(++p);
}
}
你一定看不懂。(除非你学过)
对,你要是光给我这些代码
我也看不懂
数组定义
某巨佬曰:
sa[i]:排名为i的后缀的位置
rak[i]:从第i个位置开始的后缀的排名,下文为了叙述方便,把从第i个位置开始的后缀简称为后缀i
tp[i]:基数排序的第二关键字,意义与sa一样,即第二关键字排名为i的后缀的位置
tax[i]:i号元素出现了多少次。辅助基数排序
Q:排名?
A:就是字典序啦……
而且
还有这个性质
rak[sa[i]]=i;
sa[rak[i]]=i;
如何排序
拆开每个后缀
你会发现
一开始这个后缀第一个值就是这个后缀的起始值
第二个值呢?
其实,
我们可以把一个后缀看成一个二元组
(
c
h
[
i
]
,
i
)
(ch[i], i)
(ch[i],i)
这里的
c
h
ch
ch就是字符串
对这个二元组进行基数排序
你就会感到快乐
你就会得到排名
倍增
既然是倍增,要有倍增的样子
p.s.倍增的样子?不就是暴力?
我们在进行第一次排序时
求出了排名
但是
这排名有重复
也就是有并列
所以
我们要向外扩展每个后缀
无法扩展的,补0
你已经发现
我们第一次时取每个后缀的开头
那么,就产生了许多重复名次
所以,我们可以去对比每个后缀的前(1*2=2)个字符
如果还不行
就对比前(2*2=4)个字符
以此类推
直到排名没有并列为止
思想就是这样
不难吧
但是
你看看代码……
基数排序
你肯定会问:那个Sort函数去哪里了?
它在这里:
inline void Sort() {
memset(tax, 0, sizeof(tax));
for(int i=1; i<=n; i++) tax[rak[i]]++;
for(int i=1; i<=m; i++) tax[i]+=tax[i-1];
for(int i=N; i; i--) sa[tax[rak[tp[i]]]--]=tp[i];
}
这个tax,就是桶
如果你不了解基数排序
还是baidu一下
你会问,什么叫:
s
a
[
t
a
x
[
r
a
k
[
t
p
[
i
]
]
]
−
−
]
=
t
p
[
i
]
;
sa[tax[rak[tp[i]]]--]=tp[i];
sa[tax[rak[tp[i]]]−−]=tp[i];
t
a
x
[
r
a
k
[
t
p
[
i
]
]
]
tax[rak[tp[i]]]
tax[rak[tp[i]]]就表示当第一关键字相同时,第二关键字较大的这个后缀的排名是什么
于是,后缀数组就完了。
结束语
其实
我觉得后缀数组难在看代码
思想还是比较好接受的