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("");
	}

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM