后缀数组

后缀数组

  1. 后缀,就是从字符串中的一个字符开始直到结束的字串;而后缀数组则能求出字符串中所有后缀的排名。

  2. 介绍即将登场的数组们:sa[i]记录的是排名为i的后缀是从第几个字符开始的;Rank[i]记录的是从第i个字符开始的后缀排名第几;c[]用于基数排序;

3.代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=1000005;
int n,na,Rank[N],sa[N],tmp[N],c[N];
char s[N];

 void make_sa()
{
	int j,len;
	na=max(n,256);
	memset(c,0,sizeof(c));
	for (int i=0; i<n; i++) c[Rank[i]=s[i]&0xff]++;
	for (int i=0; i<na; i++) c[i]+=c[i-1];
	for (int i=0; i<n; i++) sa[--c[Rank[i]]]=i;//初始化Rank,c,sa
	for (len=1; len<n; len<<=1)
	{
		for (int i=0; i<n; i++)//i为排名
		{
			j=sa[i]-len;
			if (j<0) j+=n;
			tmp[c[Rank[j]]++]=j;//tmp相当于sa
		}
		sa[tmp[c[0]=0]]=j=0;//sa暂时相当于Rank
		for (int i=1; i<n; i++)//i为上一轮排名
		{
			if (Rank[tmp[i]]!=Rank[tmp[i-1]]||Rank[tmp[i]+len]!=Rank[tmp[i-1]+len]) c[++j]=i;
			sa[tmp[i]]=j;//j为当前排名
		}
		memcpy(Rank,sa,sizeof(Rank)); memcpy(sa,tmp,sizeof(sa));
		if (j>=n-1) break;//优化,排名无重复则已完成排序
	}
}

 int main()
{
	scanf("%s",s); s[strlen(s)]='$';
	n=strlen(s);
	make_sa();
	for (int i=1; i<n; i++) printf("%d ",sa[i]+1); printf("\n");
	return 0;
}

LCP

后缀的排名已经完成,那么有什么用呢?LCP(最长公共前缀)就出现了。

1.H[i]数组表示排名为i的后缀与其他后缀的最长公共前缀的长度。

2.想到既然所有后缀都已排名,那么相邻的后缀相似度最高,可以得到第i个后缀与其他后缀的最长公共前缀即为其与第i-1个的最长公共前缀。

void lcp()
{
	int j,k=0;
	for (int i=0; i<n; i++) Rank[sa[i]]=i;//初始化
	for (int i=0; i<n; i++)//枚举后缀的开始位置
	{
		if (k) k--;
		j=sa[Rank[i]-1];//排名为第i-1个的后缀的开始位置
		while (s[i+k]==s[j+k]) k++;//枚举最长长度
		H[Rank[i]]=k;
	}
}
posted @ 2020-01-29 20:53  Allen_Gun  阅读(111)  评论(0编辑  收藏  举报