- 双数组字典树:通过两个数组、某种映射关系,维护一个字典树。节约了节点所占浪费的大量空间。
- 同时满足单模、多模匹配。
-
字典树的多模匹配过程
将多个字符串构建成一棵字典树,从文本串的第一个字符依次向后进行匹配,与字符串匹配的Brute_Force算法相似 -
kmp算法是暴力匹配算法失配后,文本串下一次匹配合理性的位置
-
ac自动机 = 字典树 + kmp思想,是一种数据结构,指向文本串的指针一直往后走没有回溯的过程,,而shift_and是一种算法
ac自动机
- 核心:构建失败指针,是利用它上一个节点的失败指针的下一个节点,如果这两个节点相同,就构建
#include <cstdio>
#include <cstring>
#include <sstream>
#include <ctime>
#define base 26
typedef struct Node {
const char *str; // 存字符串
struct Node *next[base], *fail;
} Node;
typedef struct Queue {
Node **data; // **
int head, tail;
} Queue;
/* queue */
Queue *initQueue(int n) {
Queue *q = (Queue *)malloc(sizeof(Queue));
q->data = (Node **)malloc(sizeof(Node *) * n);
q->head = q->tail = 0;
return q;
}
int empty(Queue *q) {
return q->head == q->tail;
}
Node *front(Queue *q) {
return q->data[q->head];
}
void push(Queue *q, Node *node) {
q->data[q->tail++] = node;
return ;
}
void pop(Queue *q) {
if (empty(q)) return ;
q->head++;
return ;
}
void clearQueue(Queue *q) {
if (q == NULL) return ;
free(q->data);
free(q);
return ;
}
/* 字典树 */
int node_cnt = 0;
Node *getNewNode() {
Node *p = (Node *)malloc(sizeof(Node));
p->str = NULL;
p->fail = NULL;
node_cnt++;
memset(p->next, 0, sizeof(Node *) * base);
return p;
}
void clear(Node *root) {
if (root == NULL) return ;
for (int i = 0; i < base; i++) {
clear(root->next[i]);
}
if (root->str) free((char *)root->str);
free(root);
return ;
}
const char *copyStr(const char *s) {
int n = strlen(s);
char *buff = (char *)malloc(n + 1);
strcpy(buff, s);
return buff;
}
void insert(Node *root, const char *s) {
Node *p = root;
for (int i = 0; s[i]; i++) {
if (p->next[s[i] - 'a'] == NULL) {
p->next[s[i] - 'a'] = getNewNode();
}
p = p->next[s[i] - 'a'];
}
p->str = copyStr(s); // str -> flag
return ;
}
/* fail创建 ac自动机匹配 */
void initBuildFailQueue(Node *root, Queue *q) {
root->fail = NULL;
for (int i = 0; i < base; i++) {
if (root->next[i] == NULL) continue;
root->next[i]->fail = root;
push(q, root->next[i]);
}
return ;
}
void build_fail(Node *root) {
Queue *q = initQueue(node_cnt);
initBuildFailQueue(root, q);
while (!empty(q)) {
Node *p = front(q);
for (int i = 0; i < base; i++) {
if (p->next[i] == NULL) continue;
Node *k = p->fail;
// 当前节点的父节点的失败指针是空节点,继续往上找
while (k != root && root->next[i] == NULL) {
k = k->fail;
}
if (k->next[i] != NULL) k = k->next[i];
p->next[i]->fail = k;
push(q, p->next[i]);
}
pop(q);
}
clearQueue(q);
return ;
}
void match_ac(Node *root, const char *s) {
Node *p = root, *q;
for (int i = 0; s[i]; i++) { // 遍历s
while (p != root && p->next[s[i] - 'a'] == NULL) {
p = p->fail;
}
if (p->next[s[i] - 'a']) p = p->next[s[i] - 'a'];
q = p;
while (q) {
if (q->str != NULL) printf("find %s\n", q->str);
q = q->fail;
}
}
return ;
}
int main() {
int n;
char s[100];
Node *root = getNewNode();
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%s", s);
insert(root, s);
}
build_fail(root);
scanf("%s", s);
match_ac(root, s);
return 0;
}