cf 235C 后缀自动机

题目大意

给定字符串\(S\)\(n<=10^5\)个串\(x_1,x_2...x_n\)(总长\(\le10^6\))
对于每个\(x_i\),输出有多少个\(S\)的子串与\(x_i\)循环流动

分析

\(S\)建自动机
对于每个\(x\)\(xx\)放进去匹配
暴力的做法是像\(LCS_2\)那样跑完后,按拓扑序遍历更新一次自动机
显然\(TLE\)

\(len\)为当前匹配长度
于是我们每匹配到一个\(len>=|x|\)就去算这个\(|x|\)长度子串出现次数
要往父亲跳直到\(min(p)<=|x|<=max(p)\)
跳完后不能回到原位置继续匹配,这样复杂度有问题
跳完后可以直接\(len=max(或|x|)\)不会影响答案

solution

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

char s[M];
int n,m;
int last,tot;
int ch[M][26];
int stp[M],fa[M];
int right[M];
int pos[M],sum[M];
int vis[M],T;

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(ch[nq],ch[q],sizeof(ch[q]));
	for(;p&&ch[p][d]==q;p=fa[p]) ch[p][d]=nq;
	return nq;
}

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

void match(){
	int p=1,len=0,i,d;
	int res=0;
	n=strlen(s+1);
	for(i=1;i<=n;i++) s[i+n]=s[i];
	for(i=1;i<=n*2;i++){
		d=s[i]-'a';
		if(!ch[p][d]){
			for(;p&&!ch[p][d];p=fa[p]);
			if(!p) p=1,len=0;
			else{
				len=stp[p]+1;
				p=ch[p][d];
			}
		}
		else{
			len++;
			p=ch[p][d];
		}
		
		if(len>=n){
			for(;n<=stp[fa[p]];p=fa[p]);
			len=stp[p];
			if(vis[p]!=T){
				vis[p]=T;
				res+=right[p];
			}
		}
	}
	printf("%d\n",res);
}

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');
		right[last]=1;
	}
	for(i=1;i<=tot;i++) sum[stp[i]]++;
	for(i=1;i<=tot;i++) sum[i]+=sum[i-1];
	for(i=1;i<=tot;i++) pos[sum[stp[i]]--]=i;
	for(i=tot;i>1;i--) right[fa[pos[i]]]+=right[pos[i]];
	
	scanf("%d",&m);
	while(m--){
		scanf("%s",s+1);
		T++;
		match();
	}
	
	return 0;
}
posted @ 2017-03-15 20:01  _zwl  阅读(196)  评论(0编辑  收藏  举报