KMP与有限状态自动机
最近在学习字符串查找算法时,遇到了KMP算法,其中关于这块的介绍较为晦涩,记录一下自己的理解
字符串查找
字符串查找是一个应用非常多的功能,无论什么语言都会拥有该功能相关的一些周边函数或者概念,比如SQL。
通常来说,普通开发日常使用的时候,最简单的算法也就是暴力查找:
1 | int M = pat.length(); // 需匹配的字符串 |
这也是非常好理解的一种算法,将原文本中的每个字符都和匹配串的第一个字符进行比较,如果不匹配就去匹配下一个,如果匹配,则继续在匹配字符串中继续查找,直到匹配到末尾则是为成功。
这个算法可以直接达到目的,但是其中仍然有一些可以优化的地方,比如在字符串AABAABAAAA
中查找字符串AABAAA
时,当匹配到AABAAB
的B
时,则必然出现匹配失败,按照暴力算法的代码,则会回退到第二个A
再次进行匹配,然而很明显,我们知道实际上完全不需要进行这么多的回退,KMP 算法则是为了解决该问题而出现的。
KMP算法
KMP算法网上大多也有介绍,基本上都是基于next[]
来判断需要进行多少个字符的回退。
在Sedgewick的《Algorithms》一书中则使用了一种不太一样的计算方法,使得回退无需依赖于原字符串,而是仅仅使用匹配字符串就可以进行回退字符个数的预计,这就要归功于有限状态自动机
有限状态自动机
概念非常简单,就是记录在有限状态下,每个状态可以迁移到的0个或多个状态。简单来说,就类似于一种固定好的有向图,当给入一个属于字母表中的字符的时候,都可以按照预先定义好的状态,按照方向转移到下一个状态中,对应KMP算法则意味着回退到什么地方。
以图中为例,当匹配字符串为ABABAC
时,有限状态机定义如下:
先暂定输入字母表为ABC
三个字符
当输入
A
的时候,A
被匹配到,状态机会认为其可以进行到状态1
,输入另外两个则无法进行匹配,则为0
当再次输入
A
的时候,B
则无法被匹配到,A
字符的状态则被回滚到上个地方1
,而输入B
被匹配记录状态为2
再次输入
A
则又可以匹配到,此时的A
会被记录状态为3
,另外两个字符则还是会进行回退当再次输入
A
的时候,B
则无法被匹配到,A
字符的状态则被回滚到上个地方1
,而输入B
被匹配记录状态为4
再次输入
A
则又可以匹配到,此时的A
会被记录状态为5
,另外两个字符则还是会进行回退再次输入
A
和B
的时候都无法被匹配到,A
还是会回滚到1
处,而B
因为此时重启状态为3
,则退回到4
的位置
其中重启状态是个比较有意思的概念,就是需要回退到的状态,由于需要右移和忽略最后一个匹配失败,回退状态会在charAt(1)
到charAt(j - 1)
之间,也就是上一个可以达到的状态。
这种基于有限状态自动机的方式无需进行查找字符串中的回退,减少了消耗,是一种高效的查找算法