KMP算法小学生理解
1.简介
看毛片算法可谓无人不知,相比于朴素字符串匹配,kmp算法将时间复杂度从o(N*M)缩减为o(N+M),实在为一大进步。其中N为模式串的长度,M为要从中寻找字符串的长度。概括的来说,我们首先对模式串进行一些列的打表,然后对M字串遍历就可以了。下面详细地讲一下这个kmp
2.kmp详解
首先,朴素算法之所以可以被优化,其实是因为在匹配的过程中做了重复运算,举个例子来说把,如果M=abcdabcde,N=abcde,如果是朴素匹配算法的话,那么当第一次匹配到e的时候发现与M串不一致于是N串归0,M串的位置+1,接着从b开始匹配,但是你会发现,bcd都不用匹配了,因为他们明显不等于a嘛。这个时候要是可以直接跳到下一个ab开始的地方就好了。这样我们就可以避免那些重复的匹配了。那么该怎么做呢?那就是分析模式串,我们通过模式串的规律来判断我们可以跳几位2,然后打个表,每次匹配到这个位置的时候错误查表,跳转。至于这个表到底怎么来的现在先不用搞清楚,只要先知道每次跳转的大小就等于已经匹配正确的位数-查到最后一个匹配正确的表上的值。实际上我们的这个表记录的是模式串前缀后缀的最大相同长度。仔细想一想的话应该能想懂之前为什么跳转的大小要那样算。如果我们将M匹配到的位置称为count1,N匹配到的位置称为count2,那么count1跳转后,count2清0,接着匹配,但是仔细想想的话这里还是有做重复运算的,因为我都知道前缀后缀有相同了我还回去匹配那些已经知道必定相同的字串干嘛呢,所以我们不妨不要动count1,然后count2=查表的值(也就是已经知道一致的个数),这样的话我们就严格保证了M串的任何一个字符都只比较了一次。当然有种特殊情况,那就是如果count2==0的话还是不匹配的话就不要count1接着不动了,这个时候就应该count1++,找到下一个字符了。额,我感觉我讲的还是很不清楚啊2333,看代码把。。。
#include <iostream>
using namespace std;
int value[1000];
void getValue(string& p)
{
int pLen = p.size();
value[0] = -1;
int k = -1;
int j = 0;
while (j < pLen - 1)
{
//p[k]表示前缀,p[j]表示后缀
if (k == -1 || p[j] == p[k])
{
++k;
++j;
value[j] = k;
}
else
{
k = value[k];
}
}
}
int main(void){
int sum=0;
string fatherStr,sonStr;
getline(cin,fatherStr);
getline(cin,sonStr);
getValue(sonStr);
int Count=0,count1;
while(count1!=fatherStr.size()) {
sum++; //sum这里我只是想记录到底匹配了多少次
if (fatherStr[count1] == sonStr[Count]) { //如果相同那就接着匹配下一个
Count++;
count1++;
if (Count == sonStr.size()) { //模式串匹配完了的话就说明找到了
cout<<sum<<endl;
cout << "find the string" << endl;
return 0;
}
} else {
if(Count==0){ //Count如果是0的话再让Count=0就没有意义了,此时应该count1++了
count1++;
}
else{
Count=value[Count]; //如果Count不为0的话就让Count从查到的表的位置接着开始匹配
}
}
}
}