AC自动机
阅读数:103 评论数:0
跳转到新版页面分类
算法/数据结构
正文
单模式VS多模式
(1)单模式
单模就是给你一个单词,然后给你一个字符串,问你这个单词是否在这个字符串中出现过(匹配)
(2)多模式
给你很多个单词,然后给你一段字符串,问你有多少个单词在这个字符串中出现过。
算法思想:用多模式串建立一个确定性的树形有限状态机,以主串作为该有限状态机的输入,使状态机进行状态的转换,当到达某些特定的状态时,说明发生模式匹配。
建立在KMP算法和字典树基础上。
算法步骤:(1)构造一棵Tire树。(2)构造失败指针(3)模式匹配过程
构造失败指针
当发现失配的字符失配的时候,跳转到fail指针指向的位置,然后再次进行匹配操作,AC自动机之所以能实现多模式匹配,就归功于Fail指针的建立。
简单的说:设这个节点上的字母为C,沿着他父亲的失败指针走,直到走到一个节点,他的儿子中也为字母为C的节点。然后把当前节点的失败指针指向那个字母也为C的儿子。如果一直走到了root都没找到,那就把失败指针指向root。具体操作起来只需要:先把root加入队列(root的失败指针指向那个字自己或NULL),这以后我们每处理一个节点,就把它的所有儿子加入队列,直到队列为空。
#define kind 26
const int MAXN = 10000000;
struct node
{
int count; //是否为单词最后一个节点
node *next[26];//Trie每个节点的26个子节点
node *fail; //失败指针
};
node *q[MAXN]; //队列,采用bfs 构造失败指针
char keyword[55];//输入单词 模式串
char str[1000010];// 需要查找的 主串
int head,tail;//队列 头尾指针
node *root;
void insert(char *word,node *root)
{
int index,len;
node *p = root,*newnode;
len = strlen(word);
for(int i=0 ;i < len ; i++ )
{
index=word[i]-'a';
if(!p->next[index])//该字符节点不存在,加入Trie树中
{
// 初始化 newnode 并 加入 Trie 树
newnode=(struct node *)malloc(sizeof(struct node));
for(int j=0;j<26;j++) newnode->next[j]=0;
newnode->count=0;newnode->fail=0;
p->next[index]=newnode;
}
p=p->next[index];//指针移动至下一层
}
p->count++; //单词结尾 节点 count + 1 做标记
}
void build_ac_automation(node *root)
{
head=0;tail=1;
q[head]=root;
node *temp,*p;
while(head<tail)//bfs构造 Trie树的失败指针
{
//算法类似 kmp ,这里相当于得到 next[]数组
//重点在于,匹配失败时,由fail指针回溯到正确的位置
temp=q[head++];
for(int i=0;i< 26 ;i ++)
{
if(temp->next[i])//判断实际存在的节点
{
// root 下的第一层 节点 都 失败指针都 指向root
if(temp==root)temp->next[i]->fail=root;
else {
//依次回溯 该节点的父节点的失败指针
//知道某节点的next[i]与该节点相同,则
//把该节点的失败指针指向该next[i]节点
//若回溯到 root 都没有找到,则该节点
//的失败指针 指向 root
p=temp->fail;//temp 为节点的父指针
while(p){
if(p->next[i]){
temp->next[i]->fail=p->next[i];
break;
}
p=p->fail;
}
if(!p)temp->next[i]->fail=root;
}
//每处理一个点,就把它的所有儿子加入队列,
//直到队列为空
q[tail++]=temp->next[i];
}
}
}
}
int query(node *root)//类似于 kmp算法。
{//i为主串指针,p为匹配串指针
int i,cnt=0,index,len=strlen(str);
node *p=root;
for(i=0; i < len ;i ++)
{
index=str[i]-'a';
//由失败指针回溯寻找,判断str[i]是否存在于Trie树种
while( !p->next[index] && p != root)p=p->fail;
p=p->next[index];//找到后 p 指向该节点
//指针回为空,则没有找到与之匹配的字符
if(!p)p=root;//指针重新回到根节点root,下次从root开始搜索Trie树
node *temp=p;//匹配该节点后,沿其失败指针回溯,判断其他节点是否匹配
while(temp != root )//匹配 结束控制
{
if(temp->count>=0)//判断 该节点是否被访问
{
//统计出现的单词个数cnt,由于节点不是单词结尾时count为0,
//故 cnt+=temp->count; 只有 count >0时才真正统计了单词个数
cnt+=temp->count;
temp->count=-1; //标记已访问
}
else break;//节点已访问,退出循环
temp=temp->fail;//回溯失败指针继续寻找下一个满足条件的节点
}
}
return cnt;
}
相关推荐
一、介绍
1、二分图
其最大的特点在于,可以将图里的顶点分为两个集合,且集合内的点没有直接关联。
2、增广路径
增广路径是指,由一个未匹配的顶点开始,经过若干个匹配顶点,最后到达对面集合的一个未匹配
MD5,即“Message-Digest Algorithm 5”(信息-摘要算法),从名字来看就知道它是从MD3、MD4发展而来的一种加密算法,其主要通过采集文件的信息摘要,从此进行计算并加密。
M
deflate是同时使用lz77算法与huffman编码的一个无损压缩算法。人们普遍认为deflate不受任何专利所制约,deflate压缩与解压缩的源代码可以在开源的zlib库中找到。
一、二叉树
Binary Search Tree
1、二叉树性质
(1)若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值。
(2)若它的右子树不空,则右子树上的所有结点的值均大于它的根结点
一、密码学的常见概念
1、代替密码
代替密码是指先建立一个替换表,加密时通过查表,将明文的每个字母依次替换为对应的字符,生成密文。
按替换表的个数又细分为:
(1)单表代替密码
即一个明文字符对应一个
零-概述
ECB模式和CBC模式是最常用的两种模式。
1、需要块对齐的模式:ECB、CBC、PCBC
2、需要初始向量的模式:除了ECB模式外,都需要初始向量。
一、ECB,电子密码本
Electro
一、概述
1、图的表示法
图G = (V, E)的表示有邻接表和邻接矩阵两种形式。通常,对于| E |远小于| V | ^ 2的稀疏图,用邻接表比较合适。若图稠密或很小,或必须很快判别给定的两个顶点是