SPOJ 8222 NSUBSTR - Substrings (后缀自动机)

NSUBSTR - Substrings

no tags 

You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as the maximal number of times that some string with length x appears in S. For example for string 'ababa' F(3) will be 2 because there is a string 'aba' that occurs twice. Your task is to output F(i) for every i so that 1<=i<=|S|.

Input

String S consists of at most 250000 lowercase latin letters.

Output

Output |S| lines. On the i-th line output F(i).

Example

Input:
ababa

Output:
3
2
2
1
1

题目大意:f(i)表示的是长度为i的子串的最大出现次数,求f(1..n).

题解:后缀自动机

S的一个子串str,right(str)表示str在s中每次出现位置的右端点组成的集合。

fa表示一个状态s的父状态,right(s)属于right(fa)

每个状态s表示的串的长度是区间(len(fa),len(s)]

每个状态s表示的串在原串中的出现次数及出现的右端点相同。

right的求法,按照parent树中深度从大到小,依次将每个状态的right集合并入他fa状态的right集合。

我们用每个状态的|right|去更新f(len(s)),然后用f(i)去更新f(i-1)

在主链上的每个节点表示的是原串的一个前缀,出现的次数应该是+1

如果一个节点+1,那么对应的他的fa链上的节点都要+1。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define N 500003
using namespace std;
int n,m;
int ch[N][30],fa[N],l[N],last,a[N];
int cnt,p,q,np,nq,root,rt[N],b[N],t[N],f[N];
char s[N];
void extend(int x)
{
	int c=a[x];
	p=last; np=++cnt; last=np;
	l[np]=x;
	for (;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
	if (!p) fa[np]=root;
	else {
		int q=ch[p][c];
		if (l[p]+1==l[q]) fa[np]=q;
		else {
			nq=++cnt; l[nq]=l[p]+1;
			memcpy(ch[nq],ch[q],sizeof ch[nq]);
			fa[nq]=fa[q];
			fa[q]=fa[np]=nq;
			for (;ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
		}
	}
}
int main()
{
	scanf("%s",s+1); n=strlen(s+1);
	last=root=++cnt;
	for (int i=1;i<=n;i++) a[i]=s[i]-'a';
	for (int i=1;i<=n;i++) extend(i);
	p=root;
	for (int i=1;i<=n;i++) p=ch[p][a[i]],rt[p]++;
	for (int i=1;i<=cnt;i++) b[l[i]]++;
//	for (int i=1;i<=cnt;i++) cout<<l[i]<<" ";
	//cout<<endl;
	for (int i=1;i<=n;i++) b[i]+=b[i-1];
	for (int i=1;i<=cnt;i++) t[b[l[i]]--]=i;
	for (int i=cnt;i;i--) rt[fa[t[i]]]+=rt[t[i]];
//	for (int i=cnt;i;i--) cout<<rt[i]<<" ";
	//cout<<endl;
	for (int i=1;i<=cnt;i++) f[l[i]]=max(f[l[i]],rt[i]);
	for (int i=n-1;i;i--) f[i]=max(f[i],f[i+1]);
	for (int i=1;i<=n;i++) printf("%d\n",f[i]);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值