前言
我也是偶尔刷知乎时看到了这个字符串匹配算法,感觉还是很棒的,也算是一边写博客,一边学习,如有不足之处还请指正。
第一种理解方法
看到这里是不是慌了一下?刚刚我去csdn转了一圈,了解到有两种理解方法,可能第一种比较好理解一点。
首先给出待匹配字符串"BBC ABCDAB ABCDABCDABDE",然后我们的子串是"ABCDABD",现在开始。首先拿出一个部分匹配表出来。部分匹配表是针对待匹配字符串而言的,一言以蔽之,即:当前字符串的前缀与后缀完全相同时的最大长度。前缀指的是除了最后一个字符外剩下的部分的组合,如ABC的前缀可以是A,也可以是AB;同理,ABC的后缀是BC或者是C。
OK那么带匹配字符串的部分匹配表是:
0,1,0,0,1,…
有没有什么快速的方法可以求这个表呢?有的,而且还很好理解:(转自https://www.cnblogs.com/c-cloud/p/3224788.html)
void makeNext(const char P[],int next[])
{
int q,k;//q:模版字符串下标;k:最大前后缀长度
int m = strlen(P);//模版字符串长度
next[0] = 0;//模版字符串的第一个字符的最大前后缀长度为0
for (q = 1,k = 0; q < m; ++q)//for循环,从第二个字符开始,依次计算每一个字符对应的next值
{
while(k > 0 && P[q] != P[k])//递归的求出P[0]···P[q]的最大的相同的前后缀长度k
k = next[k-1]; //不理解没关系看下面的分析,这个while循环是整段代码的精髓所在,确实不好理解
if (P[q] == P[k])//如果相等,那么最大相同前后缀长度加1
{
k++;
}
next[q] = k;
}
}
我真的觉得这个算法非常牛逼,以至于我要画个图,妙啊:
OK搞定了这个数组,那么剩下的就很简单了。
当做字符串匹配时,如果某个字符匹配成功,则匹配成功计数值succ+=1,同时开始匹配下一个字符。
如果匹配某个字符失败(位置是index),则开始匹配的位置向前移动x,其中x = succ - next[index-1]。
代码我就不放了,真的没啥好放的了。
代码我借用了csdn@Summit_Yue,侵删
#include<iostream>
#include<algorithm>
#include<string>
using namespace std;
void ComputePrefix(string s,int next[]){
int n = s.length();
int q,k;
next[0] = 0;
for(k=0,q=1;q<n;q++){
while(k>0 && s[k]!=s[q])
k = next[k];
if(s[k]==s[q])
k++;
next[q] = k;
}
}
void KMPMatcher(string text,string pattern) {
int n = text.length();
int m = pattern.length();
int next[pattern.length()];
ComputePrefix(pattern, next);
for(int i=0,q=0;i<n;i++) {
while(q>0 && pattern[q]!=text[i])
q = next[q];
if(pattern[q]==text[i])
q++;
if(q==m)
{
cout<<"Pattern occurs with shift "<<i-m+1<<endl;
q=0;
}
}
}
int main()
{
string s = "abcdabcdebcd";
string p ="bcd";
KMPMatcher(s, p);
cout<<endl;
return 0;
}
原文链接:https://blog.csdn.net/u011197534/article/details/78385547
第二种理解方法
第二种理解方法是我看到知乎上一位老哥写的,他的理解角度是,状态机。然后他没有用next数组,而是用256*sizeof(pat)做的,我目测写的还算不错,这里我就只贴链接了(反正我是没打算实现)
点这里
这个算法我觉得角度很棒。