2020牛客多校第一场 B题Suffix Array(结论+后缀数组)

2020牛客多校第一场 B题Suffix Array(结论+后缀数组)

B-Suffix Array

刚学后缀数组有点菜没a出来,看题解后,是个结论没发现:

• Let C_i = min_{j > i and s_j = s_i} {j - i}
• The B-Suffix Array is equivalent to the suffix array of C_1 C_2 ... C_n

这是题解的原话,我给大家翻译一下,c[i]的值是索引i后面离它最近的与s[i]同字符的距离,然后字符串的后缀数组就与这个c数组的后缀数组,这个结论只适用于只有两个字符的情况,所以题目字符也只有两种a与b,怎么证明的话大家去看官方证明吧,本人过菜,我是看半天没看明白,orz。

不过我可以给大家演示一下。(注意如果是最后的a,b的话它后面无a,b那么结果是无穷大,我这边就让他等于n了,最后一位让它加一位为n+1)。

如abaab的c数组为231556,列出c的后缀为:

231556,31556,1556,556,56,6

所以,sa数组就是312456,反过来54213就是答案了。

我的写法是把231556变成32400-1,这样比较契合我的写法,不用大改cmp。

代码,之前看过其他大佬的,都写得很多,好像不是这么做的,可能是自己想出来的神仙写法吧orz。

还有我的后缀数组板子的话用的是书上的,比大家的短很多,主要我刚学,也不清楚他们那种是不是有什么优化,希望大佬指点迷津,以下是ac代码。

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int n,c[100007];
string s;
int ran[100007],tmp[100007],sa[100007],k;
void init(){
	k=0;
	memset(ran,0,sizeof(ran));
	memset(tmp,0,sizeof(tmp));
	memset(c,0,sizeof(c));
}
bool cmp(int i,int j){
	if(ran[i]!=ran[j]) return ran[i]<ran[j];
	else{
		int ri,rj;
		if(i+k<=n){
			ri=ran[i+k];
		}
		else{
			ri=-1;
		}
		if(j+k<=n){
			rj=ran[j+k];
		}
		else{
			rj=-1;
		}
		return ri<rj;
	}
}
void get_sa(int sa[]){
	for(int i=0;i<=n;i++){
		sa[i]=i;
		if(i<n){
			ran[i]=c[i];
		}
		else{
			ran[i]=-1;
		}
	}
	for(k=1;k<=n;k*=2){
		sort(sa,sa+n+1,cmp);
		tmp[sa[0]]=0;
		for(int i=1;i<=n;i++){
			tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i]) ? 1 : 0);
		}
		for(int i=0;i<=n;i++){
			ran[i]=tmp[i];
		}
	}
}
int main(){
	while(~scanf("%d",&n)){
		cin>>s;
		int a=n,b=n;
		for(int i=n-1;i>=0;i--){
			if(s[i]=='a'){
				if(a==n)c[i]=(n-a)%n;
				else c[i]=(n-a+i)%n;
				a=i;
			}
			else{
				if(b==n)c[i]=(n-b)%n;
				else c[i]=(n-b+i)%n;
				b=i;
			}
		}
		get_sa(sa);
		for(int i=1;i<=n;i++){
			printf("%d ",sa[i]+1);
		}puts("");
	}

}

posted @ 2020-07-12 21:54  ccsu_madoka  阅读(818)  评论(1编辑  收藏  举报