【總結】 后綴數組及應用


前言

后綴數組還是很難理解的,所以直接背個板子就好了。——Anson語錄

定義

為了下面方便,給出一些定義:

int sa[N];//從小到大排序后,第i個后綴是哪一個
int rank[N];//第i個后綴是sa中的哪一個
//由上可知:sa[rank[i]]=i;
int height[N];//LCP(Suffix(sa[i]),Suffix(sa[i-1])).

方法

由於DC-3太復雜了,所以這里只介紹倍增法:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
char s[1000010];
int n,sa[1000010],rank[1000010],height[1000010],t1[1000010],t2[1000010],c[1000010];
int a[1000010];
void SAsort(int n,int m){
	int *x=t1,*y=t2;
	for(int i=1;i<=m;i++)c[i]=0;
	for(int i=1;i<=n;i++)c[x[i]=a[i]]++;
	for(int i=1;i<=m;i++)c[i]+=c[i-1];
	for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
	for(int k=1,p=0;k<=n && p<=n;k<<=1){
		p=0;
		for(int i=n-k+1;i<=n;i++)y[++p]=i;
		for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k;
		for(int i=1;i<=m;i++)c[i]=0;
		for(int i=1;i<=n;i++)c[x[y[i]]]++;
		for(int i=1;i<=m;i++)c[i]+=c[i-1];
		for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i];
		std::swap(x,y);
		p=2;x[sa[1]]=1;
		for(int i=2;i<=n;i++)
			x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
		m=p;
	}
	for(int i=1;i<=n;i++)rank[sa[i]]=i;
	int f=0;
	for(int i=1;i<=n;i++){
		int j=sa[rank[i-1]];
		if(f)f--;
		while(s[j+f]==s[i+f])f++;
		height[rank[i]]=f;
	}
		
}
int main(){
	scanf("%s",s);n=strlen(s);
	for(int i=1;i<=n;i++)a[i]=s[i-1];
	SAsort(n,10000);
	for(int i=1;i<=n;i++)
		printf("%d%c",sa[i],i==n?'\n':' ');
	return 0;
}

這樣你就可以求出SA的一些必要的數組(希望大家可以直接背模板)

應用

Problem1

有一個字符串s,求它的子串中至少出現過兩次的最長的子串。

Solution1

考慮height的定義:兩個rank值相近的字符串的prefix,那么很顯然這樣子一定比rank值遠一些的更優啊!
所以答案就是\(max(height[i])(i∈(1,n))\)

Problem2

有一個字符串s,求它的子串中至少出現過兩次的最長的子串(不可重疊)。

Solution2

二分答案然后分成很多個集合就可以了。

Problem3

給定一個字符串s,求它不同的子串的個數。

Solution3

考慮一下每一個后綴可以提供\(len-len1\)個子串,然后考慮有\(height_i\)個重復了。
直接加起來就好了

后記

其實還沒有寫完(2018.12.20 16:39)

感謝菊隊的上古PPT


免責聲明!

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



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