【总结】 后缀数组及应用

前言

后缀数组还是很难理解的,所以直接背个板子就好了。——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

posted @ 2018-12-20 16:39  cjgjh  阅读(1276)  评论(4编辑  收藏  举报