后缀数组

后缀数组

emm,被某fatmouse吐槽注册这么久都没发过博客了,那就开始写写吧。
这个就是记一下后缀数组
后缀数组与名次数组都是要字符串内所有字符的后缀按字典序排序,后缀数组记录的是某一序号对应的是哪个后缀,名次数组记录的是该后缀的对应序号
举个例子(其中sa是后缀数组,Rank是名次数组)

在这里插入图片描述
这就是将‘aabaaaab’这个字符串中的每个字符的后缀(包括它自己)排序后, Rank存字符串中每一个后缀所对应的序号, sa存每一个序号所对应的后缀。这样不难看出, 只要求出名次数组或后缀数组中的一个, 就很容易求出对应的另一个数组,即当sa[i]=k, 则Rank[k]=i。
既然知道了名次数组与后缀数组的关系,以及求出它们的方法, 那么就可以写出求这两个数组的模板了, 这里引用了复旦大学吴永辉老师的代码及注释

struct node{
	int now,nxt;
}d[maxn];
int val[maxn][2], c[maxn], Rank[maxn], sa[maxn], pos[maxn], x[maxn];
int n;
void add_value(int u, int v, int i)
{
	d[i].nxt=c[u];
	c[u]=i;
	d[i].now=v;
}
void radix_sort(int l, int r)
{
 	for(int k=1; k>=0; k--)
 	{
 		memset(c, 0, sizeof(c));
 		for(int i=r; i>=l; i++)
 			add_value(val[pos[i]][k], pos[i], i);
 			int t=0;
 			for(int i=0; i<=20000; i++)
 				for(int j=c[i]; j; j=d[j].nxt )
 					pos[++t]=d[j].now;
	 }
	 int t=0;
	 for(int i=l; i<=n; i++)
	 {
	 	if(val[pos[i]][0]!=val[pos[i-1]][0]||val[pos[i]][1]!=val[pos[i-1]][1]) t++;
	 	Rank[pos[i]]=t;
	 }
 }
void get_suffix_array()
{
	int t=1;
	while(t/2<=n){
		for(int i=1; i<=n; i++)	{
			val[i][0]=Rank[i];
			val[i][1]=(((i+t/2<=n)?Rank[i+t/2]:0));
			pos[i]=i;
		}
		radix_sort(1, n);
		t*=2;
	}
	for(int i=1; i<=n; i++) sa[Rank[i]]=i;
 } 

这个中运用到了倍增算法
倍增算法是对每个字符开始的长度为 2 k 2^k 2k( k > = 0 k>=0 k>=0)的子串进行排序, k每次*2, 且每一次的排序都是基于上一次排序得出的Rank,设开始地址为i的长 2 k 2^k 2k的子串的Rank的关键字为xy, x既是开始地址i,, 长 2 k − 1 2^{k-1} 2k1的字符串的排序 ,y为i+ 2 k − 1 2^{k-1} 2k1,长 2 k − 1 2^{k-1} 2k1的字符串的排序 ,(若y超出了字符串, 则用0补上), 对每个长 2 k 2^k 2k的字符串排名关键字xy进行基数排序, 则可得到其Rank值。当 2 k 2^k 2k>字符串长度时, 得到的Rank值就是最后结果
举例:

在这里插入图片描述
了解了这些后, 再来看一下后缀数组一个应用:求最长公共前缀
首先我们设heigtht[i]为suffix(SA[i-1])和suffix(SA[i])的最长公共前缀的长度,对于j和k,如果Rank[j]<Rank[k],则有以下性质:
suffix(j)和suffix(k)的最长公共前缀的长度是height[Rank[j]]到height[Rank[k]]的最小值。
根据这张表可以更直观的看出, 'abaaaab’和’aaab’的最长公共前缀就是从height[Rank[5]]到height[Rank[2]]的最小值1

在这里插入图片描述
那么这边也是引用一下吴永辉老师的最长公共前缀的模板

int h[maxn], height[maxn];
void get_common_prefix()
{
	memset(h, 0 ,sizeof(h));
	for(int i=1; i<=n; i++)
	{
		if(Rank[i]==1)
			h[i]=0;
		else {
			int now=0;
			if(i>1&&h[i-1]>1) now=h[i-1]-1;
			while(now+i<=n&&now+sa[Rank[i]-1]<=n&&x[now+i]==x[now+sa[Rank[i]-1]])
				now++;
			h[i]=now;
		}
	}
	for(int i=1; i<=n; i++)
		height[Rank[i]]=h[i];
 } 

后缀数组还有很多用处, 他被广泛用于字符串处理, 其优点在于
1。避免“蛮力”搜索, 便于优化算法
2。时空效率高, 程序有标准模板
emm,就到这儿吧, 溜了溜了

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值