模式匹配算法的代码只有十四行,看了两天,终于看懂了,吐槽一下,算法太经典了。
首先要了解一下串操作中模式匹配算法的作用是什么,简单的说就是从一个主串中需找特定的字符子串,要寻找的字符子串称为模式串。自然很容易想到一个方法就是:直接拿模式串在主串中移动,一旦移动到某一个位置模式子串所有的字符与主串中的字符都相同,那么就找到了字符串,该方法成为FM算法。记主串为s1s2……sn,模式串为p1p2……pm。如图一所示:
图一:FM算法串匹配
这种算法很简单,但是时间上付出了很大的代价,每一次匹配不成功,都必须回溯到原来的位置的下一个位置重新匹配,复杂度为O(m*n)。模式串匹配是在该方法的基础之上进行改进,想办法不需要在主串中回溯,即希望主串中每个字符值匹配一次,KMP算法是可以做到的,下面我们就来看看KMP。
假设拿模式串和主串进行匹配,从模式串的第一个字符开始,如果相等,则进行下一个字符的比较……一直进行下去直到两个字符不等,记录此时主串位置为i,模式串位置为j,即第i和第j个位置失配。这样我们想办法让主串的i保持不回溯,其实如果’pj-k+1pj-k+2……pj-1’=’p1p2……pk-1’(在j之前:前缀=后缀,注意这里前后缀相等的长度要取最大值),只需将模式串右移,(令next[j]=k)就可以保持i不回溯了(注意这里我们讨论从第一个字符串开始,不考虑0下标的数组)。如图二所示:
图二:KMP模式串匹配
总结起来KMP算法与FM算法的不同就是,FM在失配时需要回溯,而KMP在i-j失配时不需要回溯,只需确定新的k,使得下一次next[j]=k,重新进行匹配,一直这样下去……确定k的准则就是j之前的:前缀等于后缀pj-k+1pj-k+2……pj-1’=’p1p2……pk-1’。
下面就来谈谈模式串如何用代码实现前缀等于后缀找next[j]=k。请跟上节奏,有点绕:
因为每一个位置都有一个next值,于是可以做如下假设:设模式串第i个位置的next[j]=k,即有:
‘pj-k+1pj-k+2……pj-1’ = ’p1p2……pk-1’ (1)
接下来看一下next[j+1]如何取值?
1)如果pj=pk,则表明
’pj-k+1pj-k+2……pj-1pj’ = ’p1p2……pk-1pk’ (2)
所以next[j+1]= next[j]+1= k+1
2)如果pj != pk,则表明
‘pj-k+1pj-k+2……pj-1’ = ’p1p2……pk-1’ (3)
pj != pk (4)
用下图不同颜色表示相等的前缀和后缀
图三 :模式串示意图
这时候主模式串又可以看做自己的模式串,即拿自身的前缀去找相同的后缀。由于已经有(3)和(4