码迷,mamicode.com
首页 > 编程语言 > 详细

理解KMP算法

时间:2016-07-28 16:13:48      阅读:254      评论:0      收藏:0      [点我收藏+]

标签:

母串:S[i]

模式串:T[i]

标记数组:Next[i](Next[i]表示T[0~i]最长前缀/后缀数)

先来讲一下最长前缀/后缀的概念

例如有字符串T[6]=abcabd接下来讨论的全部是真前缀/真后缀,也就是除去串自己本身之外的前缀/后缀

T[0]=a,此时前后缀都是a那么Next[0]=1

T[0~1]=ab,此时前缀为a,后缀为b两者不等因此Next[1]=0

T[0~2]=abc,此时前缀为a,ab,后缀为bc,c同上有Next[2]=0

T[0~3]=abca,此时前缀为a,ab,abc,后缀为bca,ca,a可以看到共同部分只有a因此Next[3]=1

T[0~4]=abcab,此时前缀为a,ab,abc,abca,后缀为bcab,cab,ab,b此时最长的公共部分为ab因此Next[4]=2

T[0~5]=abcabd,此时前缀为a,ab,abc,abca,abcab,后缀为bcabd,cabd,abd,bd,d无相等部分故Next[5]=0

 

说完前后缀的概念之后再来说说KMP的核心思想

例如S=abcabcabdabba,T=abcabd匹配时的情况如下

技术分享

可以看到在S[5]的位置匹配失败,KMP的处理方式如下

技术分享

说一下原因,在S[5]的位置匹配失败后直接用S[5]与T[2]去开始匹配,因为Next[5-1]也就是Next[4]=2,因此直接从T[2]重新开始匹配

原因很简单,既然Next[4]=2那么T[0~1]既可以与S[0~1]相等,也可以与S[3~4]相等,所以可以直接从T[2]开始,这也是KMP的精妙所在,不理解的可以自己写两个串试试。所以难点就在于Next数组的实现了,具体实现过程如下

技术分享

 

 这里只介绍了核心思想,原文比较详细请见:https://www.douban.com/note/321870890/

下面给出kuangbin大神的模板

#include<cstdio>  
#include<iostream>  
#include<algorithm>
#include<math.h> 
#include<string.h>  
#include<vector> 
#include<queue>
#include<iterator>
#include<vector>
#include<set>
#define dinf 0x3f3f3f3f
typedef long long ll;
//const int Max=(1<<16)+10;
using namespace std;
#define SIZE 100000005

const int N = 100000005;
int m_next[N];
char S[N],T[N];
int slen, tlen;

void getNext()
{
    int j, k;
    j = 0; k = -1; m_next[0] = -1;
    while(j < tlen)
        if(k == -1 || T[j] == T[k])
            m_next[++j] = ++k;
        else
            k = m_next[k];

}
/*
返回模式串T在主串S中首次出现的位置
返回的位置是从0开始的。
*/
int KMP_Index()
{
    int i = 0, j = 0;
    getNext();

    while(i < slen && j < tlen)
    {
        if(j == -1 || S[i] == T[j])
        {
            i++; j++;
        }
        else
            j = m_next[j];
    }
    if(j == tlen)
        return i - tlen+1;
    else
        return -1;
}
/*
返回模式串在主串S中出现的次数
*/
int KMP_Count()
{
    int ans = 0;
    int i, j = 0;

    if(slen == 1 && tlen == 1)
    {
        if(S[0] == T[0])
            return 1;
        else
            return 0;
    }
    getNext();
    for(i = 0; i < slen; i++)
    {
        while(j > 0 && S[i] != T[j])
            j = m_next[j];
        if(S[i] == T[j])
            j++;
        if(j == tlen)
        {
            ans++;
            j = m_next[j];
        }
    }
    return ans;
}
int main()
{
    
    int TT;
    int i, cc;
    string str;
    cin>>TT;
    while(TT--)
    {
        getchar();
    
        scanf("%s %s",&T,&S);
        slen = strlen(S);
        tlen = strlen(T);
        
        cout<<"模式串T在主串S中首次出现的位置是: "<<KMP_Index()/2+1<<endl;
        cout<<"模式串T在主串S中出现的次数为: "<<KMP_Count()<<endl;
    }
    return 0;
}

 

理解KMP算法

标签:

原文地址:http://www.cnblogs.com/pter/p/5715034.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!