初学后缀数组

后缀数组就是把一个字符串的后缀排序,然后就可以再乱搞一些东东。

DC3看着就累,还是倍增好。

当初看代码觉得虽然不长但是理解起来并不容易,各种数组有各自的意义很容易搞混。

特别是要理解基数排序。每次按第二关键字排序再按第一关键字排序,可以保证在第一关键字相同时第二关键字的大小关系也正确。

白书上的数组有s(字符串)、sa、x、y、c,计算height还用到了rank。

我的代码是照着白书上的写的(发现白书上的代码有几个小的细节问题)。

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn=1e6+10;
char s[maxn];

int n,m,p;
int sa[maxn],x[maxn],y[maxn],c[maxn];
//sa当前排好的后缀 x每个后缀的rank y由第二关键字排好的后缀 c记录每种相同的个数及前缀和 
int height[maxn];

int main() {
	gets(s); n=strlen(s); m='z'+1;
	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;//sa[c[w]-1]为放入目前rank为w的可放的最后一个位置 
	for(int k=1;k<=n;k<<=1) {
		p=0;
		for(int i=n-1;i>=n-k;--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);//此时y已经无用,我们需要更新x数组必须用到曾经的x数组(判断相同的情况) 
		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&&sa[i]+k<n&&y[sa[i-1]+k]==y[sa[i]+k]? p-1:p++;
		if(p>=n) break;
		else m=p;
	}
	for(int i=0;i<p;++i) printf("%d ",sa[i]+1); printf("\n");
	int k=0;
	for(int i=0;i<n;++i) {
		if(!x[i]) {
			k=0; continue;
		}
		if(k) k--;
		int j=sa[x[i]-1];
		while(s[i+k]==s[j+k]) k++;
		height[x[i]]=k;
	}
	for(int i=1;i<n;++i) printf("%d ",height[i]);
	return 0;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值