首页 > 编程语言 >【算法】AC 自动机

【算法】AC 自动机

时间:2024-11-19 20:29:49浏览次数:1  
标签:AC int void ++ 算法 num fail 自动机 id

1. 算法简介

AC 自动机,是用来多模式匹配串的算法。最好可以做到 \(O(\sum |t_i|\times |\sigma| + |s|)\)。(预处理 \(O(\sum |t_i|\times |\sigma| )\),查询时间复杂度为 \(O(|s|)\))。

2. 算法流程

AC 自动机可以处理这样的问题:给定 \(n\) 个匹配串和一个模式串,求出模式串中出现了多少个匹配串。

首先,对于给定的匹配串 \(t_i\) 构造出 trie 树。例如:有 \(5\) 个匹配串,youisherhehis,可以构造出如下 trie 树。

image

然后对于 trie 树上连失配边,即 trie 上当前位置若失配,则可以通过走 \(fail\) 边来继续完成匹配。

\(fail\) 边的连边规则为以下:

  • 若当前点存在字符 \(ch\) 儿子,则让她的儿子连向当前失配边连向的那个点的字符 \(ch\) 儿子。

如下图:

image

这样通过遍历文本串和在 tire 树上不停地跳节点可以匹配完所有的模式串。

但是注意,统计出现的模式串的过程需要不断地跳 \(fail\) 来统计。这样暴力跳 \(fail\) 的时间复杂度为 \(O(|s|\times \sum t_i)\)。明显假掉。

我们可以利用延迟统计的思想,将贡献标记在 trie 树上,然后在树上拓扑排序,计算 \(ans_i\) 表示 \(i\) 可以被多少模式串跳到。这样的时间复杂度为 \(O(|s|)\)。

总时间复杂度为 \(O(\sum |t_i|\times |\sigma| + |s|)\)。非常优秀。

3. 算法实现

3.1 建立 trie 树

void insert(string S, int ip) {
  int p = 0;
  for (reg int i = 0; i < S.size(); ++i) {
    if(!t[p][S[i] - 'a']) {
      t[p][S[i] - 'a'] = ++idx;
    }
    p = t[p][S[i] - 'a'];
  }
  if(!id[p]) {
    id[p] = ip;
  }
  pos[ip] = id[p];
  return ;
}

3.2 计算 fail

void Fail() {
  queue<int> q;
  For(i,0,25) {
    if(t[0][i]) fail[t[0][i]] = 0, q.push(t[0][i]);
  }
  while(!q.empty()) {
    int u = q.front();
    q.pop();
    For(i,0,25) {
      if(t[u][i]) fail[t[u][i]] = t[fail[u]][i], in[t[fail[u]][i]]++, q.push(t[u][i]);
      else t[u][i] = t[fail[u]][i];
    }
  }
}

3.3 贡献标记

void query() {
  int u = 0;
  For(i,1,n) {
    u = t[u][T[i] - 'a'];
    num[u]++;
  }
  return ;
}

3.4 拓扑排序统计答案

void kahn() {
  queue<int> q;
  For(i,0,idx) {
    if(!in[i]) {
      q.push(i);
    }
  }
  while(!q.empty()) {
    int x = q.front();
    q.pop();
    ans[id[x]] = num[x];
    int y = fail[x];
    num[y] += num[x];
    if(!(--in[y])) q.push(y);
  }
  return ;
}

3.5 模版

P5357 【模板】AC 自动机

#include<bits/stdc++.h>
#define int long long
#define reg register
#define For(i,l,r) for(reg int i=l;i<=r;++i)
#define FOR(i,r,l) for(reg int i=r;i>=l;--i)

using namespace std;

bool Start;

const int N = 2e6 + 10, M = 26;

int n, m, t[N][M], id[N], pos[N], fail[N], num[N], ans[N], in[N], idx;

char T[N];

void insert(string S, int ip) {
  int p = 0;
  for (reg int i = 0; i < S.size(); ++i) {
    if(!t[p][S[i] - 'a']) {
      t[p][S[i] - 'a'] = ++idx;
    }
    p = t[p][S[i] - 'a'];
  }
  if(!id[p]) {
    id[p] = ip;
  }
  pos[ip] = id[p];
  return ;
}

void Fail() {
  queue<int> q;
  For(i,0,25) {
    if(t[0][i]) fail[t[0][i]] = 0, q.push(t[0][i]);
  }
  while(!q.empty()) {
    int u = q.front();
    q.pop();
    For(i,0,25) {
      if(t[u][i]) fail[t[u][i]] = t[fail[u]][i], in[t[fail[u]][i]]++, q.push(t[u][i]);
      else t[u][i] = t[fail[u]][i];
    }
  }
}

void query() {
  int u = 0;
  For(i,1,n) {
    u = t[u][T[i] - 'a'];
    num[u]++;
  }
  return ;
}

void kahn() {
  queue<int> q;
  For(i,0,idx) {
    if(!in[i]) {
      q.push(i);
    }
  }
  while(!q.empty()) {
    int x = q.front();
    q.pop();
    ans[id[x]] = num[x];
    int y = fail[x];
    num[y] += num[x];
    if(!(--in[y])) q.push(y);
  }
  return ;
}

bool End;

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  cerr << 1.0*(&Start-&End)/(1024576.00) << '\n';
  cin >> m;
  For(i,1,m) {
    string S;
    cin >> S;
    insert(S, i);
  }
  cin >> (T + 1);
  n = strlen(T + 1);
  Fail();
  query();
  kahn();
  For(i,1,m) cout << ans[pos[i]] << '\n';
  return 0;
}

标签:AC,int,void,++,算法,num,fail,自动机,id
From: https://www.cnblogs.com/Daniel-yao/p/18555498

相关文章

  • hackcambridge-ccleaner-app CCleaner.dat 文件解密
    ccleanerv5.70CCleaner.dat文件解密hackcambridge-ccleaner-appimportbase64fromCrypto.CipherimportBlowfishfromCrypto.Util.Paddingimportunpad,pad#k="041ULKGbv7meLDmSgUyrkw=="k="d3fRPFY0JQp5D76PyNh4ag=="#iv="e12SAq6rENg......
  • OSTrack:Joint Feature Learning and Relation Modeling for Tracking: A One-Stream F
    Abstract问题:传统的双流跟踪框架对目标提取的特征不够具体。特征提取和关系建模是分开进行的,导致算法在区分目标和背景方面的能力有限。两流、两阶段框架容易受到性能-速度困境的影响。解决:提出一种新的单流跟踪框架,OSTrack通过桥接具有双向信息流的模板搜索图像来统一特......
  • 2024年11月一区SCI-逃离优化算法Escape Algorithm-附Matlab免费代码
    引言本期介绍了一种受人群疏散行为的启发的元启发式优化算法,称为逃离优化算法EscapeAlgorithm,ESC。该算法于2024年11月最新发表在JCR1区,中科院2区TopSCI期刊 ArtificialIntelligenceReview。ESC的灵感来自于人们在紧急疏散期间的行为。本节解释人群疏散系统的背景,以及......
  • 物理hack
    声明声明文章只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负。✍......
  • 海马优化算法(SHO)优化BP神经网络原理及MATLAB代码复现
    目录0引言1数学模型2优化方式3MATLAB代码3.1伪代码3.2SHO主函数代码3.3SHO-BP0引言海马优化算法(Sea-horseoptimizer,SHO)是ShijieZhao等人于2023年提出群智能算法,该算法模拟了海马的运动、捕食和繁殖行为。在前两个阶段,SHO分别模拟了海马的不同运动模式和概......
  • 海马优化算法(SHO)优化支持向量机网络原理及MATLAB代码复现
    目录0引言1数学模型2优化方式3MATLAB代码3.1伪代码3.2SHO主函数代码3.3SHO-SVR、SHO-SVM0引言海马优化算法(Sea-horseoptimizer,SHO)是ShijieZhao等人于2023年提出群智能算法,该算法模拟了海马的运动、捕食和繁殖行为。在前两个阶段,SHO分别模拟了海马的不同运......
  • 非洲秃鹫算法(AVOA)优化支持向量机原理及MATLAB代码复现
    目录0引言1数学模型2优化方式3MATLAB代码3.1伪代码3.2AVOA主函数代码3.3AVOA-SVR,AVOA-SVM0引言非洲秃鹫算法(Africanvulturesoptimizationalgorithm,AVOA)是由BenyaminAbdollahzadeh等人于2021年基于非洲秃鹫的领导者-追随者模型捕食提出的群智能算法,AVOA通......
  • 【算法】manacher
    1.算法简介Manacher算法,俗称马拉车。是一个可以在线性时间复杂度内高效解决最大回文子串的问题。2.算法流程暴力想必大家也都会,就是枚举中心点然后暴力扩展长度。时间复杂度\(O(n^2)\)。还有就是字符串哈希+二分:枚举中心点,将暴力的扩展变成二分。因为长度越长更不能回文......
  • 如何解决 No module named 'cx_Oracle'
    错误Nomodulenamed'cx_Oracle'通常是因为在你的Python环境中没有安装cx_Oracle模块。以下是解决问题的方法:1.确认环境确保你在正确的Python环境下运行代码。如果使用虚拟环境,请激活它:sourcevenv/bin/activate#Linux/macOSvenv\Scripts\activate#Wind......
  • json解析之fastjson和jackson使用对比
    json解析之fastjson和jackson使用对比前言最近项目中需要做埋点分析,首先就需要对埋点日志进行解析处理,刚好这时候体验对比了下fastjson和jackson两者使用的区别,以下分别是针对同一个json串处理,最终的效果都是将json数据解析出来,并统一展示。一、fastjson简介?fastjson是由......