• 【洛谷P3808】【模板】AC自动机(简单版)


    题目

    题目链接:https://www.luogu.com.cn/problem/P3808
    给定 \(n\) 个模式串 \(s_i\) 和一个文本串 \(t\),求有多少个不同的模式串在文本串里出现过。
    两个模式串不同当且仅当他们编号不同。

    思路

    AC 自动机可以看做 KMP + Trie。
    首先我们将所有模式串建成一棵 Trie 树,定义 \(fail[i]\) 表示一个点 \(i\) 到根路径形成的字符串,最长出现在 Trie 树中的后缀的位置。其定义可以类比 KMP 的 next 数组。
    \(c[i][x]\) 表示:

    • 如果点 \(i\) 有字符 \(x\) 儿子,那么为这个儿子的编号。
    • 否则为点 \(i\) 到根形成的字符串最长拥有 \(x\) 儿子的后缀的 \(x\) 儿子编号。

    显然 \(fail\) 会形成一个树形结构,我们可以构建出 fail 树,然后在文本串匹配到点 \(i\) 的时候不断往其 \(fail\) 跳,并将答案加上到达的点结尾的模式串数量。
    注意要类比并查集路径压缩的方法处理 \(c\) 数组。
    显然每个点只会遍历一遍,时间复杂度 \(O(\sum^{n}_{i=1}len_i)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=1000010,M=27;
    int n,len;
    char s[N];
    
    struct ACA
    {
    	int tot,c[N][M],num[N],fail[N];
    	bool vis[N];
    	
    	void ins(char *ch)
    	{
    		int len=strlen(ch+1),p=0;
    		for (int i=1;i<=len;i++)
    		{
    			int val=ch[i]-'a'+1;
    			if (!c[p][val]) c[p][val]=++tot;
    			p=c[p][val];
    		}
    		num[p]++;
    	}
    	
    	void build()
    	{
    		queue<int> q;
    		for (int i=1;i<=26;i++)
    			if (c[0][i]) q.push(c[0][i]);
    		while (q.size())
    		{
    			int u=q.front();
    			q.pop();
    			for (int i=1;i<=26;i++)
    				if (c[u][i]) fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);
    					else c[u][i]=c[fail[u]][i];
    		}
    	}
    	
    	int query(char *ch)
    	{
    		int len=strlen(ch+1),p=0,ans=0;
    		for (int i=1;i<=len;i++)
    		{
    			int val=ch[i]-'a'+1;
    			p=c[p][val];
    			for (int j=p;j && !vis[j];j=fail[j])
    				ans+=num[j],vis[j]=1;
    		}
    		return ans;
    	}
    }AC;
    
    int main()
    {
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%s",s+1);
    		AC.ins(s);
    	}
    	AC.build();
    	scanf("%s",s+1);
    	printf("%d",AC.query(s));
    	return 0;
    }
    
  • 相关阅读:
    PHP语言结构
    时钟拖放
    CSS定位
    vi命令(转)
    数值转化Excel列字母的函数
    SCOPE_IDENTITY、IDENT_CURRENT 和 @@IDENTITY的比较
    数字转化罗马数字的函数
    在VB中如何打开“文件夹选项”对话框?
    纯VB代码取得硬盘的物理序列号 (转)
    项目经理应该做什么
  • 原文地址:https://www.cnblogs.com/stoorz/p/13537947.html
Copyright © 2020-2023  润新知