模式匹配KMP算法

关于KMP算法的原理网上有很详细的解释,我总结一下理解它的要点:

  以这张图片为例子

  

  这里我们匹配到j=5时失效了,接下来就直接比较T[2](next[5]=2)和S[5]

那为什么可以跳过朴素算法里的几次比较,而直接用T[next[j]]比较就可以呢?

  • 我们匹配过S0S1S2S3S4=T0T1T2T3T4,
  • next[5]=2,2是公共序列的最大长度了,也就是说:
  • T0T1=T3T4,但是T0T1T2≠T2T3T4,T0T1T2T3≠T1T2T3T4,
  • 那么就有S3S4=T3T4=T0T1,而S2S3S4=T2T3T4≠T0T1T2,S1S2S3S4=T1T2T3T4≠T0T1T2T3
  • 所以可以直接比较T2和S5

  Q:next数组计算的原理是什么?

  如果我们算完了next[i] ,现在要算next[i+1]了,我们让k=next[i],

  • 可以看到如果 k 和 i 位置的字符相同(T[i]和T[k]比较了,i,k再增加1,因为如果k和i位置的字符不同的话,k和i还不能增加1,见下文分析),那next[i+1]就等于k+1,代码就是:
if(T[k]==T[i])
    {
        k++;
        i++;
        next[i]=k;
    }
  • 如果不同呢?那就有next[i+1]≤next[i],不可能有更长的公共序列,
  • 而在匹配的部分里(图中灰色)有更小的一段(图中蓝色部分),是next[next[i]]前面的子串,根据next数组的含义,左右的蓝色的和粉色的子串分别相同,因为左右灰色部分是相同的,那左边的蓝色就和右边的粉色相同,
  • 如果这时Ti=Tnext[k],那它就是next[i]的值了,否则继续找更小的一段(这一部分和上面的判断是差不多的,所以只要更新k=next[k],然后继续循环就可以了),直到k=-1,那么next[i]就等于-1了,

整个的代码就是:

void get_next(const string &T,int *next){
    int i=0,k=-1;
    next[i]=k;
    while(T[i]!=‘\0‘){
        if(k==-1||T[k]==T[i])
        {
            k++;
            i++;
            next[i]=k;
        }else{
            k=next[k];
        }
    }
}

  但是其实还可以再改进,因为例如

  那么当i=3失配时,T的移动如下,

  这样没有利用前面那么多a一样的特点,导致匹配次数比较多,所以我们这样改进:

  因为T[3]失配了,而T[3]=T[2],所以T[2]也会失配,因为失配,所以还要用next寻找合适的匹配位置,所以next[3]=next[2]。在我们计算next[3]之前,next[2]已经计算出来,同理的,next[2]=next[1]=next[0]=-1。

  所以改进的next的规则多了一条:

    如果T[i]=T[i-1],next[i]=next[i-1]

  在这里,next[0]=-1,next[1]=-1,next[2]=-1,next[3]=-1,next[4]=3,代码就变成了:

void get_next(const string &T,int *next)
{
    int i=0,k=-1;
    next[i]=k;
    while(T[i]!=‘\0‘)
    {
        if(k==-1||T[k]==T[i])
        {
            k++;
            i++;
            if(T[i] != T[k])
                next[i] = k;
            else
                next[i] = next[k];
        }
        else
        {
            k=next[k];
        }
    }
}

  完整程序代码:

#include<iostream>
#include<cstring>
using namespace std;
void get_next(const string &T,int *next)
{
    int i=0,k=-1;
    next[i]=k;
    while(T[i]!=‘\0‘)
    {
        if(k==-1||T[k]==T[i])
        {
            k++;
            i++;
            if(T[i] != T[k])
                next[i] = k;
            else
                next[i] = next[k];
        }
        else
            k=next[k];
    }
}
/*
返回模式串T在主串P的第pos个字符起第一次出现的位置,
若不存在,则返回-1,下标从0开始,0<=pos<=P.size()(P的字符总个数)
*/
int KMP_index(const string &P,const string &T,int pos)
{
    int *next=new int[T.size()+2];//动态分配next数组
    get_next(T,next);
    int i=0,j=pos;
    while(i<T.size()&&j<P.size())
    {
        if(i==-1||P[j]==T[i])
        {
            j++;
            i++;
        }
        else
            i=next[i];
    }
    delete []next;
    if(i>=T.size())
        return j-i;
    return -1;
}
int main()
{
    char *T,*P;
    T=new char[100];
    P=new char[100];
    cin>>P>>T;
    cout<<KMP_index(P,T,0)<<endl;
    return 0;
}

  

时间: 2024-10-24 04:04:36

模式匹配KMP算法的相关文章

模式匹配—KMP算法中Next值求解

#include<stdio.h> #include<stdlib.h> #include<string.h> typedef struct { char *ch; int length; }HString; void StrAssign(HString &T,char chars[]); int get_next(HString T,int next[]); void main() { HString T; char chars[80]; int i,n,ne

字符串模式匹配KMP算法中的next数组算法及C++实现

一.问题描述: 对于两个字符串S.T,找到T在S中第一次出现的起始位置,若T未在S中出现,则返回-1. 二.输入描述: 两个字符串S.T. 三.输出描述: 字符串T在S中第一次出现的起始位置,若未出现,则返回-1. 四.输入例子: ababaababcbababc 五.输出例子: 5 六.KMP算法解析: KMP算法分为两步,第一步是计算next数组,第二步是根据next数组通过较节省的方式回溯来比较两个字符串. 网络上不同文章关于next数组的角标含义略有差别,这里取参考文献中王红梅<数据结构

模式匹配- KMP算法

■Knuth-Morris-Pratt(KMP)算法-听我的,别总重来. 发表于1977年的KMP算法是一种高效的匹配算法,消除了BF算法中回溯问题,即每次移动的距离可以不是1而是更大的数,也不需要回溯,BF算法的时间复杂度是O(m*n),而KMP算法的时间复杂度是O(m+n). 在BF算法中,用模式串去和目标串比较时,如果发生不匹配,就要回溯到起始位置,然后后移.KMP算法的思想是,设法利用这个已知信息,跳过前面已经比较过的位置,继续把它向后移,从而提高效率. 这样,KMP算法有以下两个要点:

串模式匹配——KMP算法

#include<iostream> using namespace std; #include<cstring> typedef struct { char ch[1000002] = {' '}; int length; }sstring;//定义ADTsstring来表示字符串的性质 char temp[1000002]={' '}; void cinstring(sstring &s,char temp[]);//输入主串和模式串,实现字符串下标表示位置 void

数据结构例程——串的模式匹配(KMP算法)

本文针对数据结构基础系列网络课程(4):串中第5课时串的模式匹配(KMP算法). 问题:串的模式匹配 KMP算法: #include <stdio.h> #include "sqString.h" void GetNext(SqString t,int next[]) /*由模式串t求出next值*/ { int j,k; j=0; k=-1; next[0]=-1; while (j<t.length-1) { if (k==-1 || t.data[j]==t.d

串模式匹配之BF和KMP算法

本文简要谈一下串的模式匹配.主要阐述BF算法和KMP算法.力求讲的清楚又简洁. 一 BF算法 核心思想是:对于主串s和模式串t,长度令为len1,len2,   依次遍历主串s,即第一次从位置0开始len2个字符是否与t对应的字符相等,如果完全相等,匹配成功:否则,从下个位置1开始,再次比较从1开始len2个字符是否与t对应的字符相等.... BF算法思路清晰简单,但是每次匹配不成功时都要回溯. 下面直接贴代码: int BF_Match(char *s, char *t) { int i=0,

字符串模式匹配之KMP算法图解与 next 数组原理和实现方案

之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 i指针,而是利用已经得到的“部分匹配”的结果将模式子串向右“滑动”尽可能远的一段距离后,继续进行比较.如果 ok,那么主串的指示指针不回溯!算法的时间复杂度只和子串有关!很好. KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的,很自然的,需要一个函数来存储匹

字符串的模式匹配——Brute-Force算法和KMP算法

子串的定位操作是要在主串S中找出一个与子串T相同的子串,通常把主串S称为目标,把子串T称为模式把从目标S中查找模式为T的子串的过程称为“模式匹配”. 1.Brute-Force算法的设计思想 Brute-Force是普通的模式匹配算法.将主串S的第1个字符和模式T的第1个字符比较,若相等,继续逐个比较后续字符:若不等,从主串的下一字符起,重新与模式的第一个字符比较,直到主串的一个连续子串字符序列与模式相等 ,返回值为S中与T匹配的子序列第一个字符的序号,即匹配成功:否则,匹配失败,返回值 0.

KMP算法 KMP模式匹配 二(串)

B - KMP模式匹配 二(串) Crawling in process... Crawling failed Time Limit:1000MS     Memory Limit:131072KB     64bit IO Format:%lld & %llu Description 输入一个主串和一个子串,用KMP进行匹配,问进行几趟匹配才成功,若没成功,则输出0 Input 输入一个主串和一个子串 Output 匹配的趟数 Sample Input ababcabcacbab abcac