SPOJ 8222 NSUBSTR(SAM)

这几天看了N多论文研究了下后缀自动机,刚开始蛋疼的看着极短的代码和clj的论文硬是看不懂,后来结合其他几篇论文研究了下,总算是明白了一些

推荐文章http://blog.sina.com.cn/s/blog_70811e1a01014dkz.html

看了几篇文章觉得还是这篇写的清晰明了,建议看几遍明白如何建SAM再看了clj的论文。clj的论文中对性质的研究比较深入

下面是clj论文里推荐的一题,题意:给一个字符串S,令F(x)表示S的所有长度为x的子串中,出现次数的最大值。求F(1)..F(Length(S)) (感谢clj的翻译>_<)

首先按顺序建SAM,一个串的|Right|就是出现次数。由于父节点的Right集合正好等于子节点Right集合的并集,于是可以拓扑排序从后往前找,然后每次再把子节点的Right加到pre节点上即可。这里拓扑排序使用了类似于计数排序的思想。见代码

#include<iostream>//SAM
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=250005<<1;
int last,tot,son[N][26],pre[N],step[N],cnt[N],Q[N],g[N],f[N];
char s[N];
void ins(int x,int m){
	int p=last,np=++tot;
	step[np]=m,last=np,g[np]++;
	for(;!son[p][x] && p!=-1;p=pre[p])	son[p][x]=np;
	if(p==-1)	return;
	else{
		int q=son[p][x];
		if(step[q]==step[p]+1)	pre[np]=q;
		else{
			step[++tot]=step[p]+1;
			int nq=tot;
			memcpy(son[nq],son[q],sizeof(son[q]));
			pre[nq]=pre[q];
			pre[q]=pre[np]=nq;
			for(;son[p][x]==q && p!=-1;p=pre[p])	son[p][x]=nq;	
		}
	}
}
int main(){
	pre[0]=-1;
	scanf("%s",s);
	int l=strlen(s);
	for(int i=0;i<l;++i)	ins(s[i]-'a',i+1);
	for(int i=1;i<=tot;++i)	cnt[step[i]]++;
	for(int i=1;i<=tot;++i)	cnt[i]+=cnt[i-1];
	for(int i=1;i<=tot;++i)	Q[cnt[step[i]]--]=i;
	for(int i=tot;i>=1;--i)	printf("%d\n",step[Q[i]]);
	for(int i=tot;i>=1;--i)	f[step[Q[i]]]=max(f[step[Q[i]]],g[Q[i]]),g[pre[Q[i]]]+=g[Q[i]];
	for(int i=1;i<=l;++i)	printf("%d\n",f[i]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值