后缀数组

本文介绍了后缀数组的基本概念,包括rk数组的定义与计算方法(通过计数排序实现倍增),以及height数组的计算。通过实际应用案例如MusicalTheme、LifeForms等展示后缀数组在字符串处理中的关键作用。

【算法简介】

 

【定义】

后缀数组是一个维护序列所有后缀的算法,具体的维护每一个后缀在所有后缀中的排名rk,和它的逆数组sa表示排名为i的数组开头是sa[i]位

以及题目中最常应用的height数组,表示排名为i和排名为i-1两个后缀的最长公共前缀

 

【操作】

1.calcsa

计算sa和rk数组,主要是基于计数排序来实现倍增的计算排名

具体实现理解了之后就是背板子了

bool cmp(int x,int y,int w)
{
	return oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[y+w];
}
void calcsa()
{
	int m=233;
	for(int i=0;i<=m;i++) cnt[i]=0;
	for(int i=1;i<=n;i++) cnt[rk[i]=a[i]]++;
	for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
	int i,p;
	for(int w=1;;w<<=1,m=p)
	{
		for(p=0,i=n;i>n-w;i--) id[++p]=i;
		for(i=1;i<=n;i++) if(sa[i]>w) id[++p]=sa[i]-w;
		for(i=0;i<=m;i++) cnt[i]=0;
		for(i=1;i<=n;i++) cnt[fz[i]=rk[id[i]]]++;
		for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(i=n;i>=1;i--) sa[cnt[fz[i]]--]=id[i];
		for(i=1;i<=n;i++) oldrk[i]=rk[i];
		for(p=0,i=1;i<=n;i++) rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
		if(p==n)
		{
			for(i=1;i<=n;i++) sa[rk[i]]=i;
			break;
		}
	}
}

2.calch

计算height数组,类似尺取的思想去计算即可

void calch()
{
	int i,k=0;
	for(i=1;i<=n;i++)
	{
		if(k) k--;
		while(a[i+k]==a[sa[rk[i]-1]+k]) k++;
		h[rk[i]]=k;
	}
}

 

【应用】

1.poj1743 Musical Theme

最长的相似不重叠子串,这里相似定义为两个串每次字符对应的差值相同

2.poj3294 Life Forms

求出现次数超过一半的最长子串

3.poj3261 Milk Patterns

求重复k次可重叠子串长度

4.SPOJ694 Distinct Substrings

求不相同的子串的个数

5.poj2406 Power Strings

字符串从头开始最多有多少个重复片段

6.ural1297 palindrome

求最长回文子串

7.poj2774 Long Long Message

两个串的最长公共连续子串

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值