KMP算法总结

参照了http://blog.csdn.net/v_july_v/article/details/7041827大神的总结

一、KMP的作用:匹配原始串和模式串,在原始串中找到最早出现模式串的坐标并返回之,如果没有找到返回-1

二、KMP的思想:对模式串搜索重复的子串,列出next[]数组,根据next[]数组在原始串和模式串匹配过程中移动模式串而非回溯原始串

三、KMP的时间复制度O(n+m),n为原始串的长度,m为模式串的长度

四、KMP的两个模板(两种写法的next[]数组是不同滴):

1、正规写法

/*其实在KMP匹配过程中,当发生两个串中字符比较不等时指针 i 不需要回溯,

而只要将 T 串向右滑动到一定位置继续进行字符间的比较。例如按此算法进行

模式串T = "abcac" 和主串S ="ababcabcabcacabca",S串中除了第3、7和10个

字符和T串中的字符比较了两次之外,其它字符和T串中的字符均只进行了一次

比较。你知道是因为什么原因吗? 以S6!=t4而失配的情况为例,因为已经得到

的匹配信息是:S2=t0,S3=t1,S4=t2,S5=t3,S6!=t4,而从T串本身得到的信

息是:t1!=t0,t2!=t0,t3=t0,这就是说,不可能存在一个从 S2或S3或S4开始

的子串和T串相等,但可能存在从S5开始的子串和T串相等,并且由于已经得到的

" S5=t3,t3=t0"信息就不需要再进行S5和t0的比较,而直接进行S6和t1的比较

即可。这里假定lena>lenb。

*/

#include<cstdio>

#include<iostream>

#include<cstring>

using namespace std;

int lena,lenb;

/*求next函数的过程是一个递推的过程:



1.首先由定义得next[0]=-1,next[1]=0;



2.假设已知next[j]=k,又T[j] = T[k],则显然有next[j+1]=k+1;



3.如果T[j]!= T[k],则令k=next[k],直至T[j]等于T[k]为止。*/

void getnext(string b,int next[])

{

    int j=0,k=-1;

    next[0]=-1;

    while(j<lenb)

    {

        if(k==-1||b[j]==b[k])   //此举旨在找寻B串中重复的东东

        {

            k++,j++;

            //如果下一个b[j]等于b[k],那下一次next[j]要回到0的位置,这是高速回位的方法

            if(b[k]!=b[j]) next[j]=k;

            else next[j]=next[k];

        }

        else k=next[k];

    }

}

int kmp(string a,string b)

{

    int next[50],i=0,j=0;

    lena=a.length();

    lenb=b.length();

    getnext(b,next);

    while(i<lena&&j<lenb)

    {

        if(j==-1||a[i]==b[j])

        {

            i++,j++;

        }

        else j=next[j];

    }

    if(j==lenb)

    return i-lenb;

    return -1;

}

int main()

{

    string a,b;

    while(cin>>a>>b)

    {

        printf("%d\n",kmp(a,b));

    }

}

倒写KMP:返回的是从右到左第一个匹配到的下标

#include<cstdio>

#include<iostream>

#include<cstring>

using namespace std;

void getnext(string b,int next[]) //寻找所需匹配字符串的next数组

{

 int len=b.size();

 len--;

 next[len]=len+1;

 int k=len+1,j=len;

 while(j>=0)

    {

        if(k==len+1||b[j]==b[k])

        {

            k--,j--;

            if(b[k]!=b[j]) next[j]=k;

            else next[j]=next[k];

        }

        else k=next[k];

    }

}

int kmp(string a,string b)

{

    int i=a.size()-1,j=b.size()-1;

    int lenb=b.size(),lena=a.size();

    int next[50];

    getnext(b,next);

    while(i>=0&&j>=0)

    {

        if(j==lenb||a[i]==b[j])

        {

            i--,j--;

        }

        else j=next[j];

    }

    if(lena-i>=b.size()) return i+1; //返回下标

    return -1;

}

int main()

{

    string a,b;

    while(cin>>a>>b)

    {

        printf("%d\n",kmp(a,b));

    }

}

 

2、覆盖函数写法(转自http://blog.csdn.net/v_july_v/article/details/7041827,再次膜拜下大神):

覆盖函数所表征的是pattern本身的性质,可以让为其表征的是pattern从左开始的所有连续子串的自我覆盖程度。比如如下的字串,abaabcaba

KMP算法总结

可能上面的图令读者理解起来还是不那么清晰易懂,其实很简单,针对字符串abaabcaba

a(-1) b(-1)a(0) a0 b(1) c(-1) a(0) b(1)a(2)

解释:

  1. 初始化为-1  
  2. b与a不同为-1   
  3. 与第一个字符a相同为0   
  4. 还是a为0   
  5. 后缀ab与前缀ab两个字符相同为1 
  6. 前面并无前缀c为-1  
  7. 与第一个字符同为0  
  8. 后缀ab前缀ab为1 
  9. 前缀aba后缀aba为2

 由于计数是从0始的,因此覆盖函数的值为0说明有1个匹配,对于从0还是从来开始计数是偏好问题,具体请自行调整,其中-1表示没有覆盖,那么何为覆盖呢,下面比较数学的来看一下定义,比如对于序列a0a1...aj-1 aj

 要找到一个k,使它满足

  a0a1...ak-1ak=aj-kaj-k+1...aj-1aj

    而没有更大的k满足这个条件,就是说要找到尽可能大k,使pattern前k字符与后k字符相匹配,k要尽可能的大,原因是如果有比较大的k存在。

    但若我们选择较小的满足条件的k,那么当失配时,我们就会使pattern向右移动的位置变大,而较少的移动位置是存在匹配的,这样我们就会把可能匹配的结果丢失。比如下面的序列,KMP算法总结

    在红色部分失配,正确的结果是k=1的情况,把pattern右移4位,如果选择k=0,右移5位则会产生错误。计算这个overlay函数的方法可以采用递推,可以想象如果对于pattern的前j个字符,如果覆盖函数值为k

    a0a1...ak-1ak=aj-kaj-k+1...aj-1aj
则对于pattern的前j+1序列字符,则有如下可能
    ⑴     pattern[k+1]==pattern[j+1] 此时overlay(j+1)=k+1=overlay(j)+1
    ⑵     pattern[k+1]≠pattern[j+1] 此时只能在pattern前k+1个子符组所的子串中找到相应的overlay函数,h=overlay(k),如果此时pattern[h+1]==pattern[j+1],则overlay(j+1)=h+1否则重复(2)过程.

#include "stdafx.h"  

#include<iostream>  

#include<string>  

#include <vector>  

using namespace std;  

  

int kmp_find(const string& target,const string& pattern)  

{  

    const int target_length=target.size();  

    const int pattern_length=pattern.size();  

    int* overlay_value=new int[pattern_length];  

    overlay_value[0]=-1;        //remember:next array's first number was -1.  

    int index=0;  

  

    //next array  

    for (int i=1;i<pattern_length;++i)  

        //注,此处的i是从1开始的  

    {  

        index=overlay_value[i-1];  

        while (index>=0 && pattern[index+1]!=pattern[i])    

        {  

            index=overlay_value[index];  

        }  

        if(pattern[index+1] == pattern[i])  

        {  

            overlay_value[i]=index+1;  

        }  

        else  

        {  

            overlay_value[i]=-1;  

        }  

    }  

  

    //mach algorithm start  

    int pattern_index=0;  

    int target_index=0;  

    while (pattern_index<pattern_length && target_index<target_length)  

    {  

        if (target[target_index] == pattern[pattern_index])  

        {  

            ++target_index;  

            ++pattern_index;  

        }   

        else if(pattern_index==0)  

        {  

            ++target_index;  

        }  

        else  

        {  

            pattern_index=overlay_value[pattern_index-1]+1;  

        }  

    }  

    if (pattern_index==pattern_length)  

    {  

        return target_index-pattern_index;  

    }   

    else  

    {  

        return -1;  

    }  

    delete [] overlay_value;  

}  

  

int main()  

{  

    string sourc="ababc";  

    string pattern="abc";  

    cout<<kmp_find(sourc,pattern)<<endl;  

    system("pause");  

    return 0;  

}  

 

你可能感兴趣的:(KMP)