zoukankan      html  css  js  c++  java
  • BZOJ3277 串 和 BZOJ3473 字符串

    字符串

    给定n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串?

    分析

    参照自为风月马前卒Candy?的题解。

    广义后缀自动机不就是把很多串的SAM建到了一个SAM上,建每个串的时候都从root开始(last=root)就行了........
    广义后缀自动机是Trie树的后缀自动机,可以解决多主串问题
    这样的在线构造算法复杂度为O(G(T)),G(T)为Trie树上所有叶子节点深度和,发现G(T)<=所有主串总长度

    对于这题,首先我们要知道几个定理

    1. 节点i表示的本质不同的字符串可以由len[i]−len[fa[i]]得到
    2. 一个串的子串 等价于 一个串所有前缀的所有后缀

    这样子串就转换为求一个串的前缀的所有后缀的问题

    前缀可以枚举,问题转换为求一个字符串的各个后缀在其他字符串中出现了多少次

    这样我们可以把广义后缀自动机建出来,然后暴力沿着parent边跑,这样可以枚举出所有后缀

    同时为了不重复枚举,我们需要记录下每个后缀是否已经被枚举过了

    这样最坏情况下复杂度为O(L^3/2),发生在n=L的时候(均值不等式啊)

    这样我们就可以知道一个状态出现的次数是否>=K,接下来我们只要统计出这个状态出现的次数就行了
    DP出f[i]为i及其Parent祖先出现次数>=k有多少字符串(注意一个状态贡献的字符串为t[par].val-t[u].val),然后在跑一遍每个字符串得到答案就行了

    然后就做完了。时间复杂度(O(L^{1.5}))

    co int N=2e5;
    string s[N];
    int last=1,tot=1;
    int ch[N][26],fa[N],len[N];
    void extend(int c){
    	int p=last,cur=last=++tot;
    	len[cur]=len[p]+1;
    	for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=cur;
    	if(!p) fa[cur]=1;
    	else{
    		int q=ch[p][c];
    		if(len[q]==len[p]+1) fa[cur]=q;
    		else{
    			int clone=++tot;
    			memcpy(ch[clone],ch[q],sizeof ch[q]);
    			fa[clone]=fa[q],len[clone]=len[p]+1;
    			fa[cur]=fa[q]=clone;
    			for(;ch[p][c]==q;p=fa[p]) ch[p][c]=clone;
    		}
    	}
    }
    int vis[N],times[N],sum[N];
    void dfs(int x){
    	if(x==1||vis[x]) return;
    	vis[x]=1;
    	dfs(fa[x]);
    	sum[x]+=sum[fa[x]];
    }
    int main(){
    	ios::sync_with_stdio(0);
    	int n,k;
    	cin>>n>>k;
    	for(int i=1;i<=n;++i){
    		cin>>s[i];
    		last=1;
    		for(int j=0;j<s[i].length();++j) extend(s[i][j]-'a');
    	}
    	// GetTimes
    	for(int i=1;i<=n;++i){
    		int now=1;
    		for(int j=0;j<s[i].length();++j){
    			now=ch[now][s[i][j]-'a'];
    			for(int t=now;t&&vis[t]!=i;t=fa[t]) vis[t]=i,++times[t];
    		}
    	}
    	for(int i=1;i<=tot;++i) sum[i]=(times[i]>=k)*(len[i]-len[fa[i]]);
    	memset(vis,0,sizeof vis);
    	for(int i=1;i<=tot;++i) dfs(i);
    	for(int i=1;i<=n;++i){
    		int ans=0,now=1;
    		for(int j=0;j<s[i].length();++j)
    			now=ch[now][s[i][j]-'a'],ans+=sum[now];
    		printf("%d ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    nodejs发送http请求
    Codeforces Round #655 (Div. 2)
    闇の連鎖 树上LCA + 树上差分
    Tree 换根dp
    「水」悠悠碧波 kmp
    HH的项链
    Educational Codeforces Round 90 (Rated for Div. 2)
    巡逻(论为什么第二次求直径要用dp)
    Codeforces Round #651 (Div. 2)
    Treap板子
  • 原文地址:https://www.cnblogs.com/autoint/p/10823942.html
Copyright © 2011-2022 走看看