【BZOJ 3277】串 广义后缀自动机

判断一个串是否在至少k个字符串里面的方法后3473一样,每添加一个新的字符就沿fail链更新。

剩下的问题就只是剩下了,每增加一个新的字符,共多出现了多少个新的字符串,如果不要求重复,那么很显然就是len[u]-len[fail[u]],这里重复的字串也需要加入答案所以只用沿fail链dfs一下,sum[u]=fail链上所有的坏和就好了

#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 200021
#include<vector>
#define LL long long
using namespace std;
int len[maxn],n,K,son[maxn][26],sz[maxn];
LL sum[maxn];
int fail[maxn],now,bg[maxn],bl[maxn],last=1,tot=1,rt=1;
char s[maxn];
vector<int>g[maxn],e[maxn];

void insert(int c){
	int p=last,np=++tot,q,nq;
	last=np;len[np]=len[p]+1;
	while(p&&!son[p][c])son[p][c]=np,p=fail[p];
	if(!p)fail[np]=rt;
	else{
		q=son[p][c];
		if(len[q]==len[p]+1)fail[np]=q;
		else{
			nq=++tot;
			len[nq]=len[p]+1;
			sz[nq]=sz[q],fail[nq]=fail[q],bl[nq]=bl[q];
			memcpy(son[nq],son[q],sizeof(son[nq]));
			fail[np]=fail[q]=nq;
			while(p&&son[p][c]==q)son[p][c]=nq,p=fail[p];
		}
	}
	g[now].push_back(np);
	while(np&&bl[np]!=now){
		sz[np]++;
		bl[np]=now;
		np=fail[np];
	}
}
void dfs(int u){
	for(int v,i=0;i<e[u].size();i++){
		v=e[u][i];
		sum[v]+=sum[u];
		dfs(v);
	}
}
int main(){
	scanf("%d%d",&n,&K);
	for(int l,i=1;i<=n;i++){
		last=rt;
		scanf("%s",s+bg[i]);l=strlen(s+bg[i]);
		bg[i+1]=bg[i]+l;
		now=i;
		for(int j=bg[i];j<bg[i+1];j++)insert(s[j]-'a');
	}
	for(int i=tot;i>1;i--){
		e[fail[i]].push_back(i);
		if(sz[i]>=K)sum[i]=len[i]-len[fail[i]];
	}sum[1]=0;
	dfs(1);
	for(int i=1;i<=n;i++){
		LL ans=0;
		for(int u,j=0;j<g[i].size();j++){
			u=g[i][j];
			ans+=sum[u];
		}
		printf("%lld ",ans);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值