SA后缀数组

SA后缀数组
sa数组:sa[i]=x表示对于所有的后缀进行排序(字典序)后,得到排名为i的以第x个字符开头的后缀
rk数组:rk[x]=i,是对于所有的后缀进行排序(字典序)后,得到以第x个字符开头的排名为i的后缀
sa[rk[x]]=rk[sa[x]]=x

常用算法:nlogn的倍增算法
对于每一个点开始,从1+2k的长度开始倍增,
相当于每次倍增都对于每一个两部分组合而成的二元组进行双关键字的排序
利用排名数组rk和oldrk不断更新,最终可以得出sa数组
用(nlogn)的排序算法,复杂度会多一只log,变成(nlog^2n)

模板


#include <bits/stdc++.h>
using namespace std;

const int N=2e5+10;

int read() {

	int x=0,f=1;
	char ch=getchar();
	while(ch<48||ch>47) {
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>=48&&ch<=57) {
		x=(x<<3)+(x<<1)+(ch^48);
		ch=getchar();
	}
	return x*f;

}

struct Ranks {

	int rk1,rk2;

} rk[N];

int sa[N],k;
char s[N];

bool cmp(int x,int y) {

	if(rk[x].rk1==rk[y].rk1) return rk[x+k].rk1<rk[y+k].rk1;
	return rk[x].rk1<rk[y].rk1;

}

int n;
void rerank() {
	for(int i=0; i<=n; i++) {
		rk[i].rk2=rk[i].rk1;
	}
}

int main() {

	cin>>(s+1);
	n=strlen(s+1);
	for(int i=1; i<=n; i++) {

		sa[i]=i;
		rk[i].rk1=s[i];

	}
	
	for(k=1; k<n; k<<=1) {

		sort(sa+1,sa+n+1,cmp);
		rerank();
		for(int t=0,i=1; i<=n; i++) {

			if(rk[sa[i]].rk2==rk[sa[i-1]].rk2&&rk[sa[i]+k].rk2==rk[sa[i-1]+k].rk2) rk[sa[i]].rk1=t;
			else rk[sa[i]].rk1=++t;

		}

	}

	for(int i=1; i<=n; i++) printf("%d ",sa[i]);

	return 0;
}


计数排序优化版


#include <bits/stdc++.h>
using namespace std;

const int N=2e6+10;

int n,k,id[N],cnt[N];
int sa[N],rk1[N],rk2[N];
char s[N];

void rerank() {
	for(int i=1; i<=n; i++) {
		rk2[i]=rk1[i];
	}
}

int main() {

	cin>>(s+1);
	n=strlen(s+1);
	int m=127,x=0;
	for(int i=1; i<=n; i++) ++cnt[rk1[i]=s[i]];
	for(int i=1; i<=m; i++) cnt[i]+=cnt[i-1];
	for(int i=n; i>=1; i--) sa[cnt[rk1[i]]--]=i;

	rerank();
	for(int i=1; i<=n; i++) {

		if(rk2[sa[i]]==rk2[sa[i-1]]) rk1[sa[i]]=x;
		else rk1[sa[i]]=++x;

	}

	for(int k=1; k<n; k<<=1,m=n) {

		memset(cnt,0,sizeof(cnt));
		memcpy(id+1,sa+1,sizeof(sa));
		for(int i=1; i<=n; i++) cnt[rk1[id[i]+k]]++;
		for(int i=1; i<=m; i++) cnt[i]+=cnt[i-1];
		for(int i=n; i>=1; i--) sa[cnt[rk1[id[i]+k]]--]=id[i];

		memset(cnt,0,sizeof(cnt));
		memcpy(id+1,sa+1,sizeof(sa));
		for(int i=1; i<=n; i++) cnt[rk1[id[i]]]++;
		for(int i=1; i<=m; i++) cnt[i]+=cnt[i-1];
		for(int i=n; i>=1; i--) sa[cnt[rk1[id[i]]]--]=id[i];

		rerank();
		x=0;
		for(int i=1; i<=n; i++) {

			if(rk2[sa[i]]==rk2[sa[i-1]]&&rk2[sa[i]+k]==rk2[sa[i-1]+k]) rk1[sa[i]]=x;
			else rk1[sa[i]]=++x;

		}

	}

	for(int i=1; i<=n; i++) printf("%d ",sa[i]);

	return 0;
}

posted @   Diamondan  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示