当前位置:网站首页>AC自动机
AC自动机
2022-07-24 21:48:00 【爱敲代码的Harrison】
AC自动机解决的问题
在前缀树上玩KMP,
假设一个字符串数组中装有若干敏感词,给定一篇大文章,AC自动机就是查出大文章中包含了哪些敏感词
AC自动机是很多类似匹配算法的一个最基本的东西
步骤
先把若干敏感词建成前缀树,(前缀树,,)
敏感词之间的组织就是一颗前缀树,但是要在前缀树上面做升级

需要在每个结点上加一条fail指针(fail指针用虚线表示),头结点的fail指针指向空,并且人为规定头结点的下级直接结点的fail指针全都指向头结点,

然后底层的fail指针如何设置?
先建完前缀树,在设置fail指针,根据宽度优先的顺序设置每一个结点的fail指针,
下图中,X结点的父结点有一条走向自己的路b,但是X的父结点的fail指针指的是头结点,头结点有走向a结点、b结点、c结点的路,就是说头结点没有走向b方向的路;于是头结点顺着fail指针往下走,来到null,null结点当然也没有走向b方向的路,所以x的fail指针直接指向头结点

然后设置下图中X结点的fail指针,跟上面一样,父结点到自己方向的路是d,但是从父结点顺着fail指针找不到走向d方向的路,所以X结点的fail指针直接指向头结点

接着设置下图中X结点的fail指针(按宽度优先的顺序设置),X的父走向自己的路位c,父的fail指针指向头结点,但是头结点有走向c方向的路,所以X的fail指针就指向此时走到c方向的结点

fail指针设置规则
第3)种情况比较复杂,



AC自动机fail指针的实质含义
如果匹配失败,在剩下的字符串中,哪一个字符串的前缀跟必须包含当前字符的后缀匹配得最长,




package com.harrison.class22;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/** * @author Harrison * @create 2022-04-02-10:21 * @motto 众里寻他千百度,蓦然回首,那人却在灯火阑珊处。 */
public class Code03_ACAutomaton {
// 前缀树的结点
public static class Node{
// 如果一个Node,end为空,说明不是结尾
// 如果end不为空,表示这个点是某个字符串的结尾,end的值就是这个字符串
public String end;
// 只有在上面的end变量不为空的时候,endUse才有意义
// 表示,这个字符串之前没有加过答案,防止重复收集答案
public boolean endUse;
public Node fail;
// 前缀树下级的路,假设大文章,所有敏感词都是小写,就可以固定26长度
public Node[] nexts;
public Node(){
endUse=false;
end=null;
fail=null;
nexts=new Node[26];
}
}
public static class ACAutomaton{
private Node root;
// 对于AC自动机来说字符一定是放在路上的,而不是放在结点上,跟前缀树一样
public ACAutomaton(){
root=new Node();
}
// 建前缀树的过程,有多少个敏感词就调用多少次insert方法
// 前缀树建好后,再调用build方法设置好所有的fail指针
public void insert(String s){
char[] str=s.toCharArray();
Node cur=root;
int index=0;
for(int i=0; i<str.length; i++){
index=str[i]-'a';
if(cur.nexts[index]==null){
cur.nexts[index]=new Node();
}
cur=cur.nexts[index];
}
cur.end=s;
}
// 注意:不是设置弹出结点自己的fail指针
// 而是弹出结点给它所有的子去设置fail指针
public void build(){
Queue<Node> queue=new LinkedList<>();
queue.add(root);
Node cur=null;
Node cfail=null;
while(!queue.isEmpty()){
// 当前结点弹出,当前结点所有的后代加入队列,当前结点给它的子去设置fail指针
// 某个父亲 cur
cur=queue.poll();
for(int i=0; i<26; i++){
// 所有的路
// cur->父亲 i号儿子,必须把i号儿子的fail指针设置好
if(cur.nexts[i]!=null){
// 如果真的有i号儿子
cur.nexts[i].fail=root;// 先设置成root,如果找到了再设置成别人
cfail=cur.fail;
while (cfail!=null){
if(cfail.nexts[i]!=null){
cur.nexts[i].fail=cfail.nexts[i];
break;
}
cfail=cfail.fail;
}
queue.add(cur.nexts[i]);
}
}
}
}
// 大文章
public List<String> containWords(String content){
char[] str=content.toCharArray();
Node cur=root;
Node follow=null;
int index=0;
List<String> ans=new ArrayList<>();
for(int i=0; i<str.length; i++){
index=str[i]-'a';// 路
// 如果当前字符在这条路上没配出来,就随着fail方向走向下条路径
while(cur.nexts[index]==null && cur!=root){
cur=cur.fail;
}
// 1) 现在来到的路径,是可以继续匹配的
// 2) 现在来到的节点,就是前缀树的根节点
cur=cur.nexts[index]!=null?cur.nexts[index]:root;
follow=cur;
while(follow!=root){
if(follow.endUse){
break;
}
// 不同的需求,在这一段之间修改
if(follow.end!=null){
ans.add(follow.end);
follow.endUse=true;
}
// 不同的需求,在这一段之间修改
follow=follow.fail;
}
}
return ans;
}
}
public static void main(String[] args) {
ACAutomaton ac = new ACAutomaton();
ac.insert("dhe");
ac.insert("he");
ac.insert("abcdheks");
// 设置fail指针
ac.build();
List<String> contains = ac.containWords("abcdhekskdjfafhasldkflskdjhwqaeruv");
for (String word : contains) {
System.out.println(word);
}
}
}
边栏推荐
- PCL点云处理之CSF布料模拟滤波(五十九)
- From A76 to A78 -- learning arm microarchitecture in change
- Circom 2.0: A Scalable Circuit Compiler
- Gradle 学习 ----Gradle 进阶说明
- What problems should be paid attention to when using a database without public ip: port?
- Easily make 2D tile map with unity tilemap - Basics
- 运动控制卡应用开发教程之调用激光振镜控制
- Description of differences between esp32c3 led PWM use and esp32
- "Paper reproduction" bidaf code implementation process (4) model training + verification
- What technical knowledge is needed to build a personal blog independently besides ECS?
猜你喜欢

ACL 2022 | comparative learning based on optimal transmission to achieve interpretable semantic text similarity

Thread pool learning

Composability and Recursion in snarkyJS

Image processing notes (1) image enhancement

How to output position synchronization of motion control

CAD text styles

Calling Laser Galvanometer control in the application and development tutorial of motion control card

GlideModule AppGlideModule和Generated API详解

Composability and Recursion in snarkyJS

Database - Metadata databasemetadata beginner
随机推荐
Calling Laser Galvanometer control in the application and development tutorial of motion control card
Go+语言
Redefine analysis - release of eventbridge real-time event analysis platform
CAD text styles
Integrated swagger learning
PCL点云处理之平面规则化(五十五)
一种兼容、更小、易用的WEB字体API
[pyspark foundation] row to column and column to row (when there are more than one column)
Leetcode 101. symmetric binary tree
Microcomputer principle: detailed explanation of CPU architecture
MySQL forced indexing
PCL点云处理之pcd文件转txt文件(单个或多个批量转换)(六十三)
Win10 solution Base64
[good question with two points]
After reading this article, I also understand this
Esp32485 air temperature and humidity test
损失函数之Diou和Ciou loss
深入理解事务
CAD copy commands
Gradle 学习 ----Gradle 进阶说明