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
",mx[i]);
	
	return 0;
}
原文地址:https://www.cnblogs.com/acha/p/6532903.html