P5357 【模板】AC自动机(拓扑排序优化查询 + fail树dfs优化查询 )

本文介绍了如何优化AC自动机的查询效率,通过拓扑排序和fail树避免重复判断,降低时间复杂度。在模式串数量较少时,使用二进制压缩并预处理,匹配过程只需遍历主串一次。同时详细解释了在trie图上进行tupo排序以及在fail树上进行DFS的方法来计算匹配次数。
摘要由CSDN通过智能技术生成

题目链接

之前学AC自动机查找的过程都是暴力跳fail,但是遇到a,aa,aaa…这种,每次只能向上跳1层,时间复杂度就直接退化成|STR|N|S| 了,要不得。

  • 朴素的查询方法是每匹配一个字符串,我们通过fail指针不断往上跳把所有的后缀给答案加上去(如果后缀中存在模式串的话)
  • 这样情况下有可能某些后缀已经被判断过了,但是又被跳到,产生重复过程
  • 我们利用fail树或tupo使得每个后缀只被判断一次
  • 另外,当模式串数量较少,比如20,可以压缩成二进制,在求fail指针的时候
    isstr[i] |= isstr[fail[i]] ,这样匹配过程只需要把主串在trie上遍历一遍就可以求出模式串被匹配的最终答案。
    (很多ac自动机+状压的题就是这样预处理的)

①trie图上tupo排序:

我们可以换一个思路,在建好AC自动机之后,当某个节点V被匹配1次,V的最长真后缀也会被匹配一次,即fail[V] 节点的答案+1 ,我们可以在查询的时候先只记录V被匹配的答案,然后把fail指针看成有向边<i,fail[i]>进行tupo ,match[ fail[v] ] += match[v]。tupo结束后,match数组就是每个模式串被匹配的次数。

细节见代码注释,

查询的时候,先不考虑fail指针,直接在trie树上匹配。

然后再利用拓扑排序,从下往上累加答案。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 2e5+10;
const int mx = 40;
const int mod = 1e9+5;
const ll inf = 34359738370;
const int INF = 1e9+7;
int tree[maxn][26];//trie树
int fail[maxn];//fail指针 指向最长的真后缀
int in[maxn];//记录每个点的入度 用于给fail树tupo排序
queue<int> q;//队列 求fail和tupo的工具人
int match[maxn];//tupo排序前 每个尾节点被匹配的次数
int idstr[maxn];//i节点 对应的模式串第一次插入时的尾节点

int mp[maxn];//n个模式串 每个模式串对应的尾节点
int tot=0;//节点编号
int ans[maxn];//tupo后  每个尾节点最终被匹配的次数  也即是答案
inline void insert(char *s,int num)
{
   
    int len=strlen(s),root=0;
    for(int i=0;i<len;i++)
    {
   
        int id=s[i]-'a';
        if(!tree[root][id]) tree[root][id]=++tot;
        root=tree[root][id];
    }
    //所有相同的模式串 都映射到同一个trie树节点
    if(!idstr[root]) idstr[root]=root;
    mp[num]=idstr[root];
}
inline void getfail()
{
   
    for(int i=0;i<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值