一个自己编写的简单AC自动机代码-----AC automata get √
最近一直在优化项目中字符串匹配的问题,于是就想起了自动机,之前也看过一些文章,一直没有实现,现在项目中要用,然后又看了一些关于AC自动机的文章,这里实现了一个简单的AC自动机的小接口,我是实现自动机状态结构采用了trie树,实现起来简单一些,但在一定程度上造成空间复杂度的增加,欢迎大家纠错和一起交流经验,下面是代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #define CHAR_EXTERN 26 //trie的next指针的最大数目,全字符时应设置为256然后和0xff进行&运算 typedef struct ac_node ac_node;
typedef struct ac_node* ac_node_p; #define QUEUE_TYPE ac_node_p //进行此宏定义,就可以把queue封装成接口使用
#define FREE_QUEUE_VALUE //queue->value为动态申请内存需要做此操作 int queue_node_num = ; //定义队列结构用于计算失效指针
typedef struct queue_node
{
QUEUE_TYPE value;
struct queue_node *next;
}queue_node; typedef struct queue
{
queue_node *head;
queue_node *tail;
}queue; /*
* queue创建结点
*/
queue_node *queue_build_node(QUEUE_TYPE value)
{
queue_node *node = (queue_node *)malloc(sizeof(queue_node)); if(node == NULL)
{
#ifdef AC_DEBUG
printf("queue node bulid error memory is full !!\n\n");
#endif
return NULL;
} node->value = value; node->next = NULL; return node;
} /*
* queue初始化
* return -1 失败 else 成功
*/
queue *queue_init()
{
queue *ac_queue = (queue *)malloc(sizeof(queue)); if(ac_queue == NULL)
{
#ifdef AC_DEBUG
printf("queue build failed memory is full\n\n");
#endif
return NULL;
} ac_queue->head = ac_queue->tail = queue_build_node(NULL); if(ac_queue->head == NULL)
{
#ifdef AC_DEBUG
printf("queue build head error memory is full\n\n");
#endif
return NULL;
} //ac_queue->head->next = ac_queue->tail; return ac_queue;
} /*
*queue 为空判断
*return 1 为空 0 不为空
*/
int queue_is_empty(queue *ac_queue)
{
if(ac_queue->head == ac_queue->tail)
{
return ;
} return ;
} /*
* queue 向队尾添加结点
*/
void queue_insert(queue *ac_queue,queue_node *node)
{
ac_queue->tail->next = node;
ac_queue->tail = node;
queue_node_num++;
#ifdef AC_DEBUG
printf("after insert the queue node num is %d :\n",queue_node_num);
#endif
} /*
*queue 提取队首结点value值
*/
QUEUE_TYPE queue_first(queue *ac_queue)
{
if(queue_is_empty(ac_queue))
{
#ifdef AC_DEBUG
printf("the queue is empty can not return head!!\n");
#endif
return NULL;
} return ac_queue->head->next->value; //队首不存值,从队首的下一个结点开始取值
} /*
*queue 队首结点出队列
*/
void queue_delete(queue *ac_queue)
{ if(queue_is_empty(ac_queue))
{
#ifdef AC_DEBUG
printf("the queue is empty we can not delete head\n\n");
#endif
return;
} queue_node *head = ac_queue->head->next; //队首不存值,从队首的下一个结点开始出队列 ac_queue->head->next = head->next; if(head == ac_queue->tail)
{//出队列为最后一个元素时将队列置空
ac_queue->tail = ac_queue->head;
} free(head); //释放队首结点内存 #ifdef AC_DEBUG
queue_node_num--;
printf("after delete the queue node num is %d :\n",queue_node_num);
#endif
} /*
*queue 释放queue内存
*/
void queue_destroy(queue *ac_queue)
{
queue_node *p = NULL;
p = ac_queue->head; while(p != NULL)
{ #ifdef FREE_QUEUE_VALUE
if(p->value != NULL)
free(p->value); //value为动态申请内存的情况下做此操作
#endif
queue_node *tmp = p->next; if(p != NULL)
free(p); p = tmp;
}
} //ac状态节点
struct ac_node
{
int final; //是否为一个模式串结尾的表示
int model; //标识该模式串为哪个模式串(如果考虑后缀子模式,此处应该改为整型链表)
ac_node *fail; //该状态节点的失效指针 struct ac_node *next[CHAR_EXTERN];
}; /*
* 创建状态节点
*/
ac_node *ac_node_build()
{
int i;
ac_node *node = (ac_node *)malloc(sizeof(ac_node)); if(node == NULL)
{
#ifdef AC_DEBUG
printf("bulid node error the memory is full !! \n\n");
#endif
return NULL;
} node->final = ;
node->model = -;
node->fail = NULL; for(i = ; i < CHAR_EXTERN; i++)
{
node->next[i] = NULL;
} return node;
} /*
* 创建trie树
* return -1 失败 else 成功
*/
int ac_trie_build(ac_node *root,char *str,int len,int model)
{
int i;
ac_node *tmp = root; if(tmp == NULL)
{
#ifdef AC_DEBUG
printf("root has not been init!!! \n\n");
#endif
return -;
} for(i = ; i < len; i++)
{ /*
ac_node *next_node = tmp->next[str[i] - 'a'];
这样写然后对next_node操作会造成trie树建立失败,由于next_node一直不为NULL
*/ int index = str[i] - 'a'; // if CHAR_EXTERN=256 index = str[i]&0xff
if(tmp->next[index] == NULL)
{
tmp->next[index] = ac_node_build(); if(tmp->next[index] == NULL)
{
#ifdef AC_DEBUG
printf("build node error in ac_trie_build !!\n");
#endif
return -;
} } tmp = tmp->next[index];
} tmp->final = ;
tmp->model = model; return ;
} /*
* 创建失效指针函数
*/ void ac_build_fail(ac_node *root,queue *ac_queue)
{
if(root == NULL || ac_queue == NULL)
{
#ifdef AC_DEBUG
printf("build ac fail pointer error -- input\n");
#endif
return ;
} int i;
queue_node *q_node = NULL;
ac_node *tmp_node = NULL;
ac_node *fail_node = NULL; q_node = queue_build_node(root);
queue_insert(ac_queue,q_node); while(!queue_is_empty(ac_queue))
{
tmp_node = queue_first(ac_queue);
#ifdef AC_DEBUG
printf("out the queue the ac node pointer is %p \n",tmp_node);
#endif
queue_delete(ac_queue);//队首元素出队列 for(i = ; i < CHAR_EXTERN; i++)
{//通过队列采用BFS(广度优先)的遍历顺序来计算当前状态每个字符的失效函数 if(tmp_node->next[i] != NULL) // if CHART_EXTERN=255 tmpnode->next[i&0xff]
{
if(tmp_node == root)
{
tmp_node->next[i]->fail = root; //第一层节点的失效指针指向根结点
}
else
{
fail_node = tmp_node->fail; //父结点的失效指针 while(fail_node != NULL)
{//当直到回到根结点时
if(fail_node->next[i] != NULL)
{//在父结点的失效指针中找到当前字符的外向边
tmp_node->next[i]->fail = fail_node->next[i];
break;
} fail_node = fail_node->fail; //继续递归
} if(fail_node == NULL)
{//找不到失效指针,则失效指针为根结点
tmp_node->next[i]->fail = root;
} } q_node = queue_build_node(tmp_node->next[i]);
queue_insert(ac_queue,q_node); //将当前层的结点插入队列继续进行广度优先遍历
#ifdef AC_DEBUG
printf("insert into a ac node into queue the state is : %c \n\n", i + 'a');
printf("insert the ac node pointer is %p\n",tmp_node->next[i]);
#endif
}
}
}
} /*
*模式匹配函数
*return -1 未匹配到任何模式 else 匹配到的当前的模式串的值
*/
int ac_query(ac_node *root,char *str,int len)
{
if(root == NULL || str == NULL)
{
return -;
} int i,match_num = ;
int index = ; ac_node *tmp_node = NULL;
ac_node *p = root; for(i = ; i < len; i++)
{
index = str[i] - 'a'; // if CHAR_EXTERN=256 index = str[i]&0xff while(p->next[index] == NULL && p != root)
{//状态在当前字符不进行转移以后的失效转移
p = p->fail;
} p = p->next[index]; //将状态进行下移 if(p == NULL)
{//未找到失效指针,从根结点开始继续
p = root;
} tmp_node = p; //计算当前状态下的匹配情况(在局部定义指针型变量经常出现内存指向问题) while(tmp_node != root)
{
if(tmp_node->final == )
{
match_num++; //tmp_node = tmp_node->fail; //匹配子模式串 return tmp_node->model; //此处不进行return 可以计算多个模式串
} tmp_node = tmp_node->fail; //匹配子模式串
} } return -;
} /*
*打印trie树
*/
void ac_trie_print(ac_node *root)
{//利用队列进行广度优先遍历并打印trie树
ac_node *ac_queue[];
int i,head,tail;
head = tail = ; memset(ac_queue,,sizeof(ac_queue)); ac_node *tmp = root; ac_queue[tail++] = tmp;
while(head != tail)
{
tmp = ac_queue[head++]; //出队列
for(i = ; i < CHAR_EXTERN; i++)
{
if(tmp->next[i] != NULL)
{
printf("%c ",i+'a');
ac_queue[tail++] = tmp->next[i]; //将当前层的所有节点入队列
}
} printf("\n"); }
} #define LIB_MODEL_NUM 10 int main()
{
#if 1
char *lib_model_str[] = {"wwwgooglecom",\
"wwwbaiducom",\
"www",\
"googlecom",\
"guoxin",\
"she",\
"her",\
"shr",\
"yes",\
"say"}; #endif #if 0 char *lib_model_str[LIB_MODEL_NUM] = {
"she",\
"he",\
"say",\
"shr",\
"her"\
};
#endif int i;
char str[];
queue *ac_queue = NULL;
ac_node *root = NULL; ac_queue = queue_init();
root = ac_node_build(); for(i = ; i < LIB_MODEL_NUM; i++)
{
ac_trie_build(root,lib_model_str[i],strlen(lib_model_str[i]),i);
} ac_trie_print(root); ac_build_fail(root,ac_queue); while()
{
printf("input the match string:\n\n"); scanf("%s",str); if(strcmp(str,"quit") == )
{
return -;
} int model = ac_query(root,str,strlen(str)); if(model == -)
{
printf("not match!!\n\n");
}
else
{
printf("match the model '%d' and the model string is %s \n\n",model,lib_model_str[model]);
}
} return ;
}
一个自己编写的简单AC自动机代码-----AC automata get √的更多相关文章
- 【AC自动机】AC自动机
Definition & Solution AC自动机是一种多模式串的字符串匹配数据结构,核心在于利用 fail 指针在失配时将节点跳转到当前节点代表字符串的最长后缀子串. 首先对 模式串 建 ...
- HDU4758 Walk Through Squares AC自动机&&dp
这道题当时做的时候觉得是数论题,包含两个01串什么的,但是算重复的时候又很蛋疼,赛后听说是字符串,然后就觉得很有可能.昨天队友问到这一题,在学了AC自动机之后就觉得简单了许多.那个时候不懂AC自动机, ...
- 字符串处理-AC自动机
估计在OJ上刷过题的都会对AC自动机这个名词很感兴趣,同样,记得去年ACM暑期集训的时候,在最后讲到字符串部分,听说了这个算法的名字之后就对于它心向往之,AC正好是Accept的简称,字面意义上的理解 ...
- Aho-Corasick automaton(AC自动机)解析及其在算法竞赛中的典型应用举例
摘要: 本文主要讲述了AC自动机的基本思想和实现原理,如何构造AC自动机,着重讲解AC自动机在算法竞赛中的一些典型应用. 什么是AC自动机? 如何构造一个AC自动机? AC自动机在算法竞赛中的典型应用 ...
- AC自动机学习笔记-1(怎么造一台AC自动机?)
月更博主又来送温暖啦QwQ 今天我们学习的算法是AC自动机.AC自动机是解决字符串多模匹配问题的利器,而且代码也十分好打=w= 在这一篇博客里,我将讲解AC自动机是什么,以及怎么构建一个最朴素的AC自 ...
- [专题总结]AC自动机
其实前面的模板也不是1A,我在题库里提前做过,也不必在意罚时,刚开始我在做别的专题 裸模板我就不说了,各个博客讲解的很明白 void insert(string s){ ,len=s.size(); ...
- 浅析 AC 自动机
目录 简述 AC 自动机是什么 AC 自动机有什么用 AC 自动机·初探 AC 自动机·原理分析 AC 自动机·代码实现 AC 自动机·更进一步 第一题 第二题 第三题 从 AC 自动机到 fail ...
- AC 自动机学习笔记
虽然 NOIp 原地爆炸了,目前进入 AFO 状态,但感觉省选还是要冲一把,所以现在又来开始颓字符串辣 首先先复习一个很早很早就学过但忘记的算法--自动 AC AC自动机. AC 自动机能够在 \(\ ...
- AC自动机
AC自动机,全称Aho-Corasick自动机.如果没记错的话好像就是前缀自动机. 其实AC自动机就是KMP上树的产物.理解了KMP,那AC自动机应该也是很好理解的. 与KMP类似,AC自动机也是扔一 ...
随机推荐
- Mysql中TO_DAYS函数
原文地址:https://blog.csdn.net/sinat_19569023/article/details/50417273 SQL博大精深 TO_DAYS函数 返回一个天数! 啊哈?什么天数 ...
- Python Tkinter 文本框(Entry)
Python Tkinter 文本框用来让用户输入一行文本字符串. 你如果需要输入多行文本,可以使用 Text 组件. 你如果需要显示一行或多行文本且不允许用户修改,你可以使用 Label 组件. 语 ...
- python实践项目七:正则表达式版本的strip()函数
描述:写一个函数,它接受一个字符串,做的事情和 strip()字符串方法一样.如果只传入了要去除的字符串, 没有其他参数, 那么就从该字符串首尾去除空白字符:否则, 函数第二个参数指定的字符将从该字符 ...
- 【转帖】编译-O 选项对性能提升作用
编译-O 选项对性能提升作用 https://www.cnblogs.com/pigerhan/p/3526889.html GCC -O 选项 这个选项控制所有的优化等级.使用优化选项会使编译过程耗 ...
- Spring Boot取消默认tomcat启动,打成war包发布到服务器运行
一.设置打包方式 在pom.xml中设置打包格式 <packaging>war</packaging> 二.取消Spring Boot的tomcat <!--部署成war ...
- python列表生成式、键盘输入及类型转换、字符串翻转、字母大小写、数组广播、循环语句等基础问题
Python知识总结 1.列表生成式 在实际开发过程中,当需要获取一个连续列表时,可直接使用range(3,10),但是如果获取该列表中每个数据的平方时,通常可以通过for循环来解决这个问题,如下 ...
- Git手册(一):基本操作
Git小册 本手册参考自runoob及其他网络资源,仅用于学习交流 Git工作流程 一般工作流程 1.克隆 Git 资源作为工作目录. 2.在克隆的资源上添加或修改文件. 3.如果其他 ...
- ASP.NET WebAPI 连接数据库
ASP.NET Web API 是一种框架,用于轻松构建可以访问多种客户端(包括浏览器和移动设备)的 HTTP 服务. ASP.NET Web API 是一种用于在 .NET Framework 上构 ...
- NIO(2):Channel
Channel可以理解为铁轨,Buffer是铁轨上的火车.铁轨的两端连接这文件描述符或者说文件的缓冲区和程序运行时的内存.借助NIO是一种更加符合OS底层文件系统的调用方式,使用NIO可以用更小的开销 ...
- .NET母版实例2(UI页面)
8.<div id="navlist"> <asp:SiteMapDataSource ID="SiteMapDataSource1" ...