首页 > 其他分享 >SA后缀数组

SA后缀数组

时间:2023-01-14 17:56:13浏览次数:40  
标签:cnt 后缀 id int 数组 -- SA sa

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

常用算法:nlogn的倍增算法
对于每一个点开始,从\(1+2^k\)的长度开始倍增,
相当于每次倍增都对于每一个两部分组合而成的二元组进行双关键字的排序
利用排名数组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;
}

标签:cnt,后缀,id,int,数组,--,SA,sa
From: https://www.cnblogs.com/Diamondan/p/17052284.html

相关文章

  • Cesium用wsad进行场景漫游(九)
    2023-01-14先看效果,wsadqe控制方向升降,鼠标拖动屏幕也可以控制方向  整理下思路:1.使用movement变量控制是否进行漫游2.1进行漫游则先将enableRotate等全部取消2......
  • 利用lodash对(对象)数组去重
    使用场景:根据数(对象)组中的id或者其他属性去重,或者对象中的所有属性值相同的去重。传统方法:通过数组的some进行逐项判断;用了lodash之后发现还是很香的。import{isEqual......
  • id_rsa/id_rsa.pub/authorized_keys之间的区别说明
    id_rsa/id_rsa.pub/authorized_keys之间的区别说明公私钥方式登录就是为了让两个linux机器之间使用ssh不需要用户名和密码。采用了数字签名RSA或者DSA来完成这个操作。假......
  • c++ 数组
              ......
  • 数组元素移动到指定位置
    letdata=[{name:1},{name:2},{name:3}]//arr:原数组,a:某个对象当前位置,b:某个对象想要移动到的位置functionMove(arr,a,b){letarr_temp=[].concat(arr);......
  • php 将二维数组处理成以某一列为key,某一列为value的一维数组
    $list=[0=>['id'=>1001,'name'=>'张三'],1=>['id'=>2091,'name'=>'李四']];array_combine(arr......
  • Java数组动态扩容和动态缩减
    数组动态扩容:packagecom;importjava.lang.String;importjava.util.Scanner;publicclassLinghu{publicstaticvoidmain(String[]args){intarr[]={1,2,3......
  • SAP STMS传输请求报错无法重新传输请求
    1.问题描述  在传输请求号的时候,第一次传输到测试系统是传输一半,报错;用表E070取消传输后,再次传输报错该请求号已经传输,无法再次传输。2.解决方案  把这个请求重新加入......
  • 数组
    数组定义数组是相同类型数据的有序集合数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成其中,每个数据称作一个数组元素,每个数据元素可以通过一......
  • SAP 交货单与HU指派关系数据不一致问题的解决方案
    SAP交货单与HU指派关系数据不一致问题的解决方案  我所在的项目是一个超大型的GlobalSAP项目,客户是一家跨国企业巨头,其SAP系统早已实施十几年了。除了SAP系统,客户还......