字符串匹配算法4:AC算法(1)理解
1. 背景(问题的提出)
假设您有一个文本包括 n个单词words 和 大量关键字 有m 个关键字keywords,请问你如何快速找到所有关键字?
首先想到的是简单的暴力匹配,两个for循环遍历查找
即如下所示:
//遍历所有单词 n个 words
//遍历所有关键字 m个 keywords
for(int i =0; i < n; i++)
{
for(int j =0; j < m; j++)
{
if(keywords[j] == words[i])
{
printf("find keywords[j] %s ",keywords[j]);
}
}
}
这样时间复杂度为O(n*m),是非常缓慢的,
那么:有没有更高效的算法实现呢?
2. AC算法
2.1 AC算法-定义
1975年,贝尔实验室的Alfred Aho和Margaret Corasick发现了一种只扫描一遍文本就能完成这项任务的算法,该算法依据他们的名字命名为Aho-Corasick匹配算法,简称AC算法。
2.2 AC算法-特点
- 只扫描一遍文本就能完成这项任务的算法
- 通过有限自动机巧妙地将字符比较转化为状态转移
2. AC算法-时间复杂度
算法的时间复杂度与关键字的数目无关,只跟文本长度有关,
其时间复杂度为O(n),优于O(n*m),匹配效率提升m倍。
3. AC算法实现步骤
3.1 两步:
第一步,我们将多个关键字构造成一个有限状态模式匹配机。
第二步,我们将文本字符串作为输入送入模式匹配机进行匹配。
ac自动机本质是一颗带状态的字典树
3.1 构建模式匹配机(三个核心函数)
在构建模式匹配机阶段,AC算法需要建立三个核心函数,分别为
- 转向函数goto
- 失效函数failure
- 输出函数output
4. 例子说明
下面通过关键字{he, she, his, hers}来介绍匹配机构建过程。
4.1 转向函数(g)
- 定义: 转向函数(g):
指的是一种状态之间的转向关系。 - 公式:
g(pre, x) = next,状态pre在输入一个字符x后转换为状态next, - 例子:
如 g(1, e) = 2。 - 问题:
如果在模式串中不存在这个的转换,如g(5, r),怎么办呢?这个需要由失效函数来处理。
4.2 失效函数(f)
- 定义:
指的是在失效情况下状态之间的转向关系 - 切换根据
根据模式含有其他模式的前缀自动切换到某个状态 - 上图说明
如上图模式串1含有模式串2的前缀,当匹配到a位置时,模式串1匹配失效,则跳转到模式串2进行匹配,而不用回到初始状态(0号状态)重新开始匹配。
4.2.1 she和hers例子
she模式是含有hers模式的前缀he,当在状态5匹配失效时跳转到状态2继续匹配。
4.2.2 递归推导
-
失效跳转状态可通过递归推导获得:
f(与状态0直连状态) = 0 //f(1)=f(3)=0
f(current) = g(f(pre),x) //f(4)=g(f(3),h)=g(0,h)=1 -
举例说明:
比如g(5, r)=fail,g(5, r)=g(f(5),r),f(5)=g(f(4),e)=g(g(f(3),h),e)
状态3对应字符s,没有其他模式的前缀,切换到状态0,即f(3)=0
所以:
f(5)=g(f(4),e)=g(g(f(3),h),e)=g(g(0,h),e)=g(1,e)=2,
g(5, r)=g(f(5),r)=g(2,r)=8
4.2.3 {he, she, his, hers}的失效函数
状态转移图的失效函数转移函数:
i 1 2 3 4 5 6 7 8 9
f(i)0 0 0 1 2 0 3 0 3
转移图如下所示:
4.3 输出函数(output)
-
定义
输出函数: 指的是状态和模式串之间的一种关系。 -
说明: output(i)={P},表示当状态机达到状态i时,模式串P中的所有模式串已经完成匹配。比如达到状态5,则模式串she和he匹配成功。
-
输出函数如下图所示:
i output(i)
2 {he}
5 {she,he}
7 {his}
9 {hers}
4.4 完整模式匹配机(图示)
5. 匹配例子(hisshers)
- 假设待匹配文本为"hisshers"
- 那么状态转移为:
0->1->6-> 7->3 -> 0->3 ->4-> 5->2 ->8->9,文本仅扫描了一遍,依次输出结果"his"、“she”、“he”、“he”、“hers”。
6. 代码实现
代码实现请看第篇博客
字符串匹配算法4:AC算法(2)代码实现
https://blog.csdn.net/lqy971966/article/details/106359472