返回
字典树到自动机——AC自动机的构造、应用与实现
闲谈
2024-02-22 13:10:27
字典树
简介
字典树(Trie树)是一种基于字符串的树形数据结构。它可以用于快速检索和插入字符串,并支持前缀匹配和通配符匹配等多种操作。
特点
- 节点上保存着某个字符。
- 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
- 一个节点的所有子节点路径代表的字符串具有公共前缀。
- 如果某个节点的所有子节点都是叶节点,那么该节点就是最后一个字符的父节点。
应用
- 词频统计
- 拼写检查
- 字符串匹配
- 数据压缩
自动机
简介
自动机(Finite Automaton,FA)是一种抽象的数学模型,用于一组状态、状态之间的转换和状态的输入和输出。自动机可以用来解决许多计算机科学问题,如词法分析、语法分析和模式匹配。
特点
- 由有限个状态组成。
- 每个状态都有一个或多个输入符号。
- 当自动机处于某个状态时,如果收到一个输入符号,它将根据该输入符号转移到另一个状态。
- 某些状态被指定为接受状态,当自动机处于接受状态时,它将接受输入的字符串。
应用
- 词法分析
- 语法分析
- 模式匹配
- 自然语言处理
- 机器学习
字典树到自动机
转换过程
将字典树转换为自动机(AC自动机)的过程如下:
- 将字典树的根节点作为自动机的初始状态。
- 对于字典树中的每个节点,创建一个新的状态。
- 对于字典树中的每个节点,将该节点的每个子节点与一个输入符号相关联,并将该输入符号对应的状态设置为该子节点的状态。
- 将字典树中的所有叶节点标记为接受状态。
应用
- 字符串匹配
- 文本搜索
- 数据挖掘
- 自然语言处理
实现
C++实现
class TrieNode {
public:
TrieNode* children[26];
bool isEndOfWord;
TrieNode() {
for (int i = 0; i < 26; i++) {
children[i] = nullptr;
}
isEndOfWord = false;
}
};
class Trie {
public:
TrieNode* root;
Trie() {
root = new TrieNode();
}
void insert(string word) {
TrieNode* curr = root;
for (char c : word) {
int index = c - 'a';
if (curr->children[index] == nullptr) {
curr->children[index] = new TrieNode();
}
curr = curr->children[index];
}
curr->isEndOfWord = true;
}
bool search(string word) {
TrieNode* curr = root;
for (char c : word) {
int index = c - 'a';
if (curr->children[index] == nullptr) {
return false;
}
curr = curr->children[index];
}
return curr->isEndOfWord;
}
};
class ACAutomata {
public:
Trie* trie;
ACAutomata() {
trie = new Trie();
}
void buildFailureLinks() {
queue<TrieNode*> q;
q.push(trie->root);
while (!q.empty()) {
TrieNode* curr = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
TrieNode* child = curr->children[i];
if (child != nullptr) {
TrieNode* failure = curr;
while (failure != nullptr && failure->children[i] == nullptr) {
failure = failure->failure;
}
if (failure == nullptr) {
child->failure = trie->root;
} else {
child->failure = failure->children[i];
}
if (child->failure->isEndOfWord) {
child->isEndOfWord = true;
}
q.push(child);
}
}
}
}
void search(string text) {
TrieNode* curr = trie->root;
for (char c : text) {
int index = c - 'a';
while (curr != nullptr && curr->children[index] == nullptr) {
curr = curr->failure;
}
if (curr == nullptr) {
curr = trie->root;
} else {
curr = curr->children[index];
}
if (curr->isEndOfWord) {
// Do something when a pattern is found
}
}
}
};
Python实现
class TrieNode:
def __init__(self):
self.children = {}
self.isEndOfWord = False
class Trie:
def __init__(self):
self.root = TrieNode()
def insert(self, word):
curr = self.root
for char in word:
if char not in curr.children:
curr.children[char] = TrieNode()
curr = curr.children[char]
curr.isEndOfWord = True
def search(self, word):
curr = self.root
for char in word:
if char not in curr.children:
return False
curr = curr.children[char]
return curr.isEndOfWord
class ACAutomata:
def __init__(self):
self.trie = Trie()
def buildFailureLinks(self):
queue = [self.trie.root]
while queue:
curr = queue.pop(0)
for char in curr.children:
child = curr.children[char]
failure = curr
while failure and char not in failure.children:
failure = failure.failure
if failure:
child.failure = failure.children[char]
if child.failure.isEndOfWord:
child.isEndOfWord = True
queue.append(child)
def search(self, text):
curr = self.trie.root
for char in text:
while curr and char not in curr.children:
curr = curr.failure
if curr:
curr = curr.children[char]
if curr.isEndOfWord:
# Do something when a pattern is found
a = ACAutomata()
a.trie.insert("he")
a.trie.insert("she")
a.trie.insert("his")
a.trie.insert("hers")
a.buildFailureLinks()
a.search("ahis")