【模板】后缀数组SA

参考题目:UOJ35


解析:

这次贴的代码有点多,除了最裸的暴力,基本上能够想到的其他各种复杂度能够过和不能过的我都去写了一遍。

但是解析可能要不知道什么时候更了。。。

之后会把后缀数组所有用途和性质都更新过来的。


代码:

哈希二分+快速排序(复杂度O(nlog⁡2n)O(n\log^2n)O(nlog2n)):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

cs int N=100005;
cs unsigned ll p=3379,mod=773577411;
unsigned ll Pow[N];

struct Hash_Array{
	unsigned ll Hash[N];
	Hash_Array(){Pow[0]=1;for(int re i=1;i<N;++i)Pow[i]=Pow[i-1]*p%mod;}
	inline void init(char *cs s,cs int &len){
		Hash[0]=0;for(int re i=1;i<=len;++i)Hash[i]=(Hash[i-1]*p%mod+s[i])%mod;
	}
	inline unsigned ll substr(int l,int r){
		return (Hash[r]-Hash[l-1]*Pow[r-l+1]%mod+mod)%mod;
	}
};

namespace SA{
	Hash_Array Hash;
	int sa[N],rk[N],ht[N],len;
	char s[N];
	inline bool rk_cmp(cs int &i,cs int &j);
	inline void init();
};

inline bool SA::rk_cmp(cs int &i,cs int &j){
	int l=0,r=len-max(i,j)+1;
	while(l<r){
		int mid=(l+r)>>1;
		if(Hash.substr(i,i+mid)==Hash.substr(j,j+mid))l=mid+1;
		else r=mid;
	}
	return s[i+l]<s[j+l];
}

inline void SA::init(){
	Hash.init(s,len);
	for(int re i=1;i<=len;++i)sa[i]=i;
	sort(sa+1,sa+len+1,rk_cmp);
	for(int re i=1;i<=len;++i)rk[sa[i]]=i;
	for(int re i=1,k=0,j;i<=len;ht[rk[i++]]=k)
	for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
}

signed main(){
	scanf("%s",SA::s+1);
	SA::len=strlen(SA::s+1);
	SA::s[SA::len+1]='\0';
	SA::init();
	for(int re i=1;i<=SA::len;++i)printf("%d ",SA::sa[i]);pc('\n');
	for(int re i=2;i<=SA::len;++i)printf("%d ",SA::ht[i]);pc('\n');
	return 0;
}

倍增+快排(复杂度O(nlog⁡2n)O(n\log^2n)O(nlog2n)):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

cs int N=100005;

namespace SA{
	char s[N<<1],t[N];
	int rk[N<<1];
	int sa[N],ht[N],len;
	int now;
	inline bool rk_cmp(cs int &i,cs int &j);
	inline bool first_sort(cs int &i,cs int &j);
	inline void init();
}

inline bool SA::rk_cmp(cs int &i,cs int &j){
	if(rk[i]^rk[j])return rk[i]<rk[j];
	return rk[i+(1<<(now-1))]<rk[j+(1<<(now-1))];
}

int tmp[N];
inline void SA::init(){
	for(int re i=1;i<=len;++i)sa[i]=i,t[i]=s[i];
	sort(t+1,t+len+1);
	int h=unique(t+1,t+len+1)-t-1;
	for(int re i=1;i<=len;++i)rk[i]=lower_bound(t+1,t+h+1,s[i])-t;
	for(now=1;(1<<(now-1))<=len;++now){
		sort(sa+1,sa+len+1,rk_cmp);
		re int cnt=0;
		for(int re i=1;i<=len;++i){
			if(rk_cmp(sa[i],sa[i-1])||rk_cmp(sa[i-1],sa[i]))++cnt;
			tmp[sa[i]]=cnt;
		}
		for(int re i=1;i<=len;++i)rk[i]=tmp[i];
		if(cnt==len)break;
	}
	for(int re i=1,k=0,j;i<=len;ht[rk[i++]]=k)
	for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
}

signed main(){
	scanf("%s",SA::s+1);
	SA::len=strlen(SA::s+1);
	SA::s[SA::len+1]='\0';
	SA::init();
	for(int re i=1;i<=SA::len;++i)printf("%d ",SA::sa[i]);pc('\n');
	for(int re i=2;i<=SA::len;++i)printf("%d ",SA::ht[i]);pc('\n');
	return 0;
}

倍增+计数排序(复杂度O(nlog⁡n)O(n\log n)O(nlogn)):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

cs int N=100005;

namespace SA{
	char s[N];
	int sa[N],rk[N],ht[N];
	int bin[N],len;
	inline void radix_sort(int *cs x,int *cs y,int m,int n);
	inline void init();
}

inline void SA::radix_sort(int *cs x,int *cs y,int m,int n){
	memset(bin+1,0,sizeof(int)*(m));
	for(int re i=1;i<=n;++i)++bin[x[i]];
	for(int re i=1;i<=m;++i)bin[i]+=bin[i-1];
	for(int re i=n;i;--i)sa[bin[x[y[i]]]--]=y[i];
}

inline void SA::init(){
	int *x=rk,*y=ht;
	for(int re i=1;i<=len;++i)x[i]=s[i],y[i]=i;
	int n=len,m=128;
	radix_sort(x,y,m,n);
	for(int re i=1,cnt=0;i<=n&&cnt<n;i<<=1){
		cnt=0;
		for(int re j=n-i+1;j<=n;++j)y[++cnt]=j;
		for(int re j=1;j<=n;++j)if(sa[j]>i)y[++cnt]=sa[j]-i;
		radix_sort(x,y,m,n);
		swap(x,y);
		x[sa[1]]=1;cnt=1;
		for(int re j=2;j<=n;++j)x[sa[j]]=((y[sa[j]]==y[sa[j-1]])&&(y[sa[j]+i]==y[sa[j-1]+i])?cnt:++cnt);
		m=cnt;
	}
	for(int re i=1;i<=n;++i)rk[sa[i]]=i;
	for(int re i=1,k=0,j;i<=len;ht[rk[i++]]=k)
	for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
}

signed main(){
	scanf("%s",SA::s+1);
	SA::len=strlen(SA::s+1);
	SA::s[SA::len+1]='\0';
	SA::init();
	for(int re i=1;i<=SA::len;++i)printf("%d ",SA::sa[i]);pc('\n');
	for(int re i=2;i<=SA::len;++i)printf("%d ",SA::ht[i]);pc('\n');
	return 0;
}

DC3(复杂度O(n)O(n)O(n),常数较大):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc gechar
#define pc putchar
#define cs const

cs int N=100005;
namespace SA{
	char s[N];
	int sa[N*3],rk[N],ht[N],len;
	int v[N*3],bin[N*3],wa[N*3],wb[N*3];
	int a[N*3];
	inline bool cmp_0(int *cs s,int a,int b);
	inline bool cmp_12(int k,int *cs s,int a,int b);
	inline void radix_sort(int *cs s,int *cs a,int *cs b,int n,int m);
	inline void dc3(int *cs s,int *cs sa,int n,int m);
	inline void init();
}

inline bool SA::cmp_0(int *cs s,int a,int b){
	return s[a]==s[b]&&s[a+1]==s[b+1]&&s[a+2]==s[b+2];
}

inline bool SA::cmp_12(int k,int *cs s,int a,int b){
	return s[a]<s[b]||s[a]==s[b]&&(k==2?cmp_12(1,s,a+1,b+1):v[a+1]<v[b+1]);
}

inline void SA::radix_sort(int *cs s,int *cs a,int *cs b,int n,int m){
	memset(bin,0,sizeof(int)*m);
	for(int re i=0;i<n;++i)++bin[s[a[i]]];
	for(int re i=1;i<m;++i)bin[i]+=bin[i-1];
	for(int re i=n-1;~i;--i)b[--bin[s[a[i]]]]=a[i];
}

inline void SA::dc3(int *cs s,int *cs sa,int n,int m){
	#define F(x) ((x)/3+((x)%3==1?0:tb))
	#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
	
	int *sn=s+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,cnt=0;
	s[n]=s[n+1]=0;
	for(int re i=0;i<n;++i)if(i%3!=0)wa[tbc++]=i;
	radix_sort(s+2,wa,wb,tbc,m);
	radix_sort(s+1,wb,wa,tbc,m);
	radix_sort(s,wa,wb,tbc,m);
	
	cnt=1,sn[F(wb[0])]=0;
	for(int re i=1;i<tbc;++i)
	sn[F(wb[i])]=cmp_0(s,wb[i-1],wb[i])?cnt-1:cnt++;
	if(cnt<tbc)dc3(sn,san,tbc,cnt);
	else for(int re i=0;i<tbc;++i)san[sn[i]]=i;
	for(int re i=0;i<tbc;++i)if(san[i]<tb)wb[ta++]=san[i]*3;
	if(n%3==1)wb[ta++]=n-1;
	radix_sort(s,wb,wa,ta,m);
	for(int re i=0;i<tbc;++i)v[wb[i]=G(san[i])]=i;
	re int i=0,j=0;
	for(cnt=0;i<ta&&j<tbc;++cnt)
	sa[cnt]=cmp_12(wb[j]%3,s,wa[i],wb[j])?wa[i++]:wb[j++];
	while(i<ta)sa[cnt++]=wa[i++];
	while(j<tbc)sa[cnt++]=wb[j++];
	
	#undef F
	#undef G 
}

inline void SA::init(){
	for(int re i=1;i<=len;++i)a[i]=s[i];
	a[len+1]=0;
	dc3(a+1,sa,len+1,128);
	for(int re i=1;i<=len;++i)rk[++sa[i]]=i;
	for(int re i=1,k=0,j=0;i<len;ht[rk[i++]]=k)
	for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
}

signed main(){
	scanf("%s",SA::s+1);
	SA::len=strlen(SA::s+1);
	SA::init();
	for(int re i=1;i<=SA::len;++i)printf("%d ",SA::sa[i]);pc('\n');
	for(int re i=2;i<=SA::len;++i)printf("%d ",SA::ht[i]);pc('\n');
	return 0;
}

基于诱导排序的SA-IS(复杂度O(n)O(n)O(n),常数较小):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

cs int N=100005; 
namespace SA{
	char s[N];
	int sa[N],ht[N],rk[N],a[N],len;
	int wa[N],wb[N];
	bool t[N<<1];
	inline bool islms(int i,bool *cs t);
	inline void induced_sort(int *cs s,int *cs sa,int len,int siz,int sigma,bool *cs t,int *cs b,int *cs cb,int *cs p);
	inline void sais(int *cs s,int *cs sa,int len,bool *cs t,int *cs b,int *cs b1,int sigma);
	inline void init();
}

inline bool SA::islms(int i,bool *cs t){
	return i>0&&t[i]&&!t[i-1];
}

inline void SA::induced_sort(int *cs s,int *cs sa,int len,int siz,int sigma,bool *cs t,int *cs b,int *cs cb,int *cs p){
	memset(b,0,sizeof(int)*sigma);
	memset(sa,-1,sizeof(int)*len);
	for(int re i=0;i<len;++i)++b[s[i]];
	cb[0]=b[0];
	for(int re i=1;i<sigma;++i)cb[i]=cb[i-1]+b[i];
	for(int re i=siz-1;~i;--i)sa[--cb[s[p[i]]]]=p[i];
	for(int re i=1;i<sigma;++i)cb[i]=cb[i-1]+b[i-1];
	for(int re i=0;i<len;++i)if(sa[i]>0&&!t[sa[i]-1])sa[cb[s[sa[i]-1]]++]=sa[i]-1;
	cb[0]=b[0];
	for(int re i=1;i<sigma;++i)cb[i]=cb[i-1]+b[i];
	for(int re i=len-1;~i;--i)if(sa[i]>0&&t[sa[i]-1])sa[--cb[s[sa[i]-1]]]=sa[i]-1;
}

inline void SA::sais(int *cs s,int *cs sa,int len,bool *cs t,int *cs b,int *cs b1,int sigma){
	int *cb=b+sigma,siz=0,cnt=0,p=-1;
	t[len-1]=1;
	for(int re i=len-2;i>=0;--i)t[i]=s[i]==s[i+1]?(t[i+1]):(s[i]<s[i+1]);
//	for(int re i=len-2;~i;--i)t[i]=s[i]<s[i+1]||(s[i]==s[i+1]&&t[i+1]);
	for(int re i=1;i<len;++i)if(t[i]&&!t[i-1])b1[siz++]=i;
	induced_sort(s,sa,len,siz,sigma,t,b,cb,b1);
	for(int re i=siz=0;i<len;++i)if(islms(sa[i],t))sa[siz++]=sa[i];
	for(int re i=siz;i<len;++i)sa[i]=-1;
	for(int re i=0;i<siz;++i){
		re int x=sa[i];
		for(int re j=0;j<len;++j){
			if(p==-1||s[x+j]!=s[p+j]||t[x+j]!=t[p+j]){
				++cnt;
				p=x;
				break;
			}
			else if(j>0&&(islms(x+j,t)||islms(p+j,t)))break;
		}
		sa[siz+(x>>=1)]=cnt-1;
	}
	for(int re i=len-1,j=len-1;i>=siz;--i)if(sa[i]>=0)sa[j--]=sa[i];
	int *s1=sa+len-siz,*b2=b1+siz;
	if(cnt<siz)sais(s1,sa,siz,t+len,b,b1+siz,cnt);
	else for(int re i=0;i<siz;++i)sa[s1[i]]=i;
	for(int re i=0;i<siz;++i)b2[i]=b1[sa[i]];
	induced_sort(s,sa,len,siz,sigma,t,b,cb,b2);
}

inline void SA::init(){
	for(int re i=1;i<=len;++i)a[i]=s[i];
	sais(a+1,sa,len+1,t,wa,wb,128);
	rk[0]=0;sa[0]=len+1;
	for(int re i=1;i<=len;++i)rk[++sa[i]]=i;
	for(int re i=1,k=0,j=0;i<len;ht[rk[i++]]=k)
	for(k?--k:0,j=sa[rk[i]-1];s[i+k]==s[j+k];++k);
}

signed main(){
	scanf("%s",SA::s+1);
	SA::len=strlen(SA::s+1);
	SA::s[SA::len+1]='\0';
	SA::init();
	for(int re i=1;i<=SA::len;++i)printf("%d ",SA::sa[i]);pc('\n');
	for(int re i=2;i<=SA::len;++i)printf("%d ",SA::ht[i]);pc('\n');
	return 0;
}

转载于:https://www.cnblogs.com/zxyoi/p/10047084.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值