KMP算法第一次看起来稍微有点复杂,其实原理还是很简单的,想清楚就理解了。
举个简单的栗子:
主串s:abcabfd
模式串t:abcabe
如果使用比较原始的方法,一个个字符进行比较,当进行到主串的i=5,s[5]=f的位置,发现与j=5,t[5]=e不匹配,则只能重新从i=2开始,与j=1再进行匹配。
而实际上,由于模式串的特点:abcabe,这个串里有两个ab,而主串f前也有个ab,则我们可以直接将f与j=3,t[3]=c进行对比,因为前面的ab是一样的,不用多一次比较了。
所以KMP算法实际上就是利用了模式串的特点,如果模式串中有较大的重复,则可以省略很多的判断,因为在主串中i是至少不动的,不用像原始的算法那样回溯。
KMP算法分为两个部分:
1).KMP的主体
2).next数组
1)的作用就是对两个串进行模式匹配,但1)需要用到2)中的next数组,因此在进行匹配前先要构建出一个next数组。
next数组的作用就类似于上面栗子中,当判断到s[5]与t[5]不相等时,直接查找next[5],next[5]指向的是f应该比较的下一个字符,这里next[5]=2,因为f前的ab与e前的ab是已知相等的,所以直接从t[2]互相对比就可以。
next数组的构建:
考虑模式串t,模式串的i与j位置:
若t[i]=t[j],则可知(1~i)与(j-i+1~j)间完全相等,则对next[j+1],也就是当主串不与模式串的第j+1个字符相等时,应该对比的是第i个元素的下一个,也就是next[j+1]=i+1;
若t[i]!=t[j],但可知(1~i-1)与(j-i+1~j-1)间完全相等,则对j个元素,应该首先与第next[j]个元素再对比一次,如果这个时候相等,则next[j+1]=next[j]+1;
否则继续找下一个,也就是第next[next[j]]个元素。
在得到next数组之后,剩下的KMP主体就很简单了,只要i与j对应的元素对比,如果s[i]==s[j],则i++,j++;否则则i与next[j]个元素对比......
代码:
可以看到,代码分了两部分:
一部分为KMP主体,另一部分为next数组的创建。
另外,对于next数组的创建中有这么一部分:
他们的作用是:
比如模式串为:aab
创建next时,next[0]自然等于-1,而next[1],如果没有这部分,将被设置为0,也就是说如果主串与模式串的i=1部分不相等时,还会去找t[0]对比,而实际上t[0]与t[1]都是a,因此是没有必要的,应该直接设置为-1。
if(t[i]!=t[j]) next[i]=j; else next[i]=next[j];
class Solution { public: int KMP(string s,string t) { vector<int> next=get_next(t); int i=0;int j=0; while(i<s.size()&&j<t.size()) { if(j==-1||s[i]==s[j]) { i++; j++; } else j=next[j]; } if(j<t.size()) return 0; else return i; } vector<int> get_next(string t) { vector<int> next(t.size(),-1); int i=0;int j=-1; while(i<(t.size()-1)) { if(j==-1||t[i]==t[j]) { i++; j++; if(t[i]!=t[j]) next[i]=j; else next[i]=next[j]; } else j=next[j]; } return next; } };