首先推荐matrix的博文KMP博文
我们如何在一个输入字符串中匹配一个模式串呢?
首先,我们可能想到最简单的方法:
string S; // 假设 S的长度为m
string pattern; // 假设 pattern的长度为n
int i = 0, j = 0;
while ( 1 ) {
if (S[j+i] == pattern[j])
j++;
else {
i++;
j = 0; // 这里,如果不匹配发生,我们永远从模式串头开始匹配。
}
if (i >= S.size())
return false;
if (j == pattern.size() )
return true;
}
(这里我还是觉得换成两个for循环最直观)
我们分析一下上面解法的复杂度。让我们分析一下最极端的情况,我们每次都匹配到模式串的倒数第一位
当我们要匹配最后一位时,发现不匹配,这时我们经历了n-1步。
这时,我们让j指针回到头,让i指针后移一位,开始匹配。极端情况下,每一次都是这种情况,复杂度为O(mn)
我们举个例子:
假设我们的输入串为aaaaaaaaaaaaaaaa...
我们的模式串为aab
这时,我们感觉被忽悠了,一看就不可能匹配上呀,没错,但是计算机要一步步傻乎乎的执行到最后才知道。
怎么办?我们如何改进我们的算法?
我们考虑一个模式串的例子:
abaaba
我们来观察一下这种模式串的pattern
发现aba既是前缀,又是后缀。这个性质可太好了,为什么好呢?
例如我们的输入串为abaabccc,我们来模拟一下匹配的过程
aabaabacc
abaaba
^
aabaabacc
abaabab
^
aabaabacc
abaabab
^
aabaabacc
abaabab
^
aabaabacc
abaabab
^
aabaabacc
abaabab
^
当我们看下一个字符的时候,c和b不匹配怎么办?
我们需要把j指针移到最开头吗?
当然不需要了!
aabaabacc
abaabab
^
只需要移动到这里就行了!
为什么可以呢?
因为模式串的匹配前缀为abaaba时,aba既是前缀也是后缀,这时候,我们只需要把j指针移动到这个
既是前缀又是后缀的串的末尾就行了,从下一个开始匹配就可以减少我们的计算量了。
这时候又有一个问题,如果有很多既是前缀又是后缀的串,j指针回到哪呢?
当然是最长的串的末尾了!!!(有谁想多匹配几次做无用功呢?)
这样,我们就已经推出大名鼎鼎的KMP算法了!!!
KMP算法分为两步,预处理和匹配
预处理时实际上是找模式串中每个位置i之前的最长既是前缀又是后缀串的长度。
这就类似于动态规划的思想了。(两个指针算不算双指针呢?)
匹配时,就按照我们已经构建好的失配指针,让j指针移动到该有的位置就好了。
我们再来看一下复杂度,由于在匹配时,我们只需要一遍扫描输入串的复杂度即可,在预处理时扫描一遍模式串
所以一共为O(m+n)。
KMP思想就是这么简单!
拓展:如果了解了KMP的思想,那么多模式串匹配也就容易了,无非就是从一个链变成一个树了吗(trie树)
有兴趣的小伙伴可以马上去了解一下多模式串匹配,真的非常相似哟~
本文深入浅出地介绍了KMP算法的原理与实现过程,并通过实例分析展示了如何利用模式串的特性提高字符串匹配效率。
2240

被折叠的 条评论
为什么被折叠?



