spoj 8222 NSUBSTR 求长度为x的子串中出现次数最大值 SAM

题目大意

给一个字符串S
令F(x)表示S的所有长度为x的子串中
出现次数的最大值。
求F(1)..F(Length(S))

分析

一个节点\(x\)的长度有\(~~(max(fa),max(x)]\)
出现次数为\(|Right(x)|\)
\((max(fa),max(x)]\)的出现次数都\(\ge |Right(x)|\)

做法

注意到对于一个点\(x\)的祖先链,长度是[1..max(fa)]
而且他们的\(|Right()|\)\(\ge |Right(x)|\)
所有更新时对于\(x\)我们直接更新\([1...max(x)]是可以的\)
只更新到\(max(x)\),像后缀和那样求答案就好了

solution

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;
const int M=524288;

int go[M][26];
int fa[M];
int stp[M];
int sum[M];
int pos[M];
int right[M];
int last,tot;
char s[M];
int mx[M];
int n;

int newnode(int ss){
	stp[++tot]=ss;
	return tot;
}

int ext(int p,int q,int d){
	int nq=newnode(stp[p]+1);
	fa[nq]=fa[q];
	fa[q]=nq;
	memcpy(go[nq],go[q],sizeof(go[q]));
	for(;p&&go[p][d]==q;p=fa[p]) go[p][d]=nq;
	return nq;
}

int sam(int p,int d){
	int np=go[p][d];
	if(np) return (stp[p]+1==stp[np]) ? np : ext(p,np,d);
	else{
		np=newnode(stp[p]+1);
		right[np]=1;
		for(;p&&!go[p][d];p=fa[p]) go[p][d]=np;
		if(!p) fa[np]=1;
		else{
			int q=go[p][d];
			fa[np]= (stp[p]+1==stp[q]) ? q : ext(p,q,d);
		}
	}
	return np;
}

int main(){

	int i;
	
	scanf("%s",s+1);
	n=strlen(s+1);
	
	last=tot=1;
	for(i=1;i<=n;i++) last=sam(last,s[i]-'a');
	for(i=1;i<=tot;i++) sum[stp[i]]++;
	for(i=1;i<=n;i++) sum[i]+=sum[i-1];
	for(i=1;i<=tot;i++) pos[sum[stp[i]]--]=i;
	for(i=tot;i>0;i--) right[fa[pos[i]]]+=right[pos[i]];
	for(i=1;i<=tot;i++) mx[stp[i]]=max(mx[stp[i]],right[i]);
	for(i=n;i>0;i--) mx[i]=max(mx[i],mx[i+1]);
	for(i=1;i<=n;i++) printf("%d\n",mx[i]);
	
	return 0;
}
posted @ 2017-03-10 20:07  _zwl  阅读(134)  评论(0编辑  收藏  举报