zoukankan      html  css  js  c++  java
  • 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
    ",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;
    }
    
  • 相关阅读:
    基础技术
    Luogu1438 无聊的数列(线段树)
    树状数组从入门到入土
    左偏树
    PA2014-Final Zarowki(堆)
    BZOJ1455罗马游戏
    【小米oj】 海盗分赃
    【小米oj】 最少交换次数
    【小米oj】 大胃王的烦恼
    【小米oj】 不一样的排序
  • 原文地址:https://www.cnblogs.com/acha/p/6556062.html
Copyright © 2011-2022 走看看