Algorithm --> KMP算法

KMP算法

一、传统字符串匹配算法

/* 
 * 从s中第sIndex位置开始匹配p
 * 若匹配成功,返回s中模式串p的起始index
 * 若匹配失败,返回-1
 */
int index(const std::string &s, const std::string &p, const int sIndex = 0)
{
    int i = sIndex, j = 0;

    if (s.length() < 1 || p.length() < 1 || sIndex < 0)
    {
        return -1;
    }

    while (i != s.length() && j != p.length())
    {
        if (s[i] == p[j])
        {
            ++i;
            ++j;
        }
        else
        {
            i = i - j + 1;
            j = 0;
        }
    }
    return j == p.length() ? i - j: -1;
}

另外一种简单匹配:

int Index_BF ( char S [ ], char T [ ], int pos )
{
  /* 若串 S 中从第pos(S 的下标0≤pos<StrLength(S))个字符
  起存在和串 T 相同的子串,则称匹配成功,返回第一个
  这样的子串在串 S 中的下标,否则返回 -1    */

  int i = pos, j = 0;
  while ( S[i+j] != ''&& T[j] != '')
  {
    
if ( S[i+j] == T[j] )       j++; // 继续比较后一字符     else     {       i ++;
      j = 0; // 重新开始新的一轮匹配     }  
  }

  if ( T[j] == '')      return i; // 匹配成功 返回下标   else      return -1; // 串S中(第pos个字符起)不存在和串T相同的子串 } // Index_BF

二、KMP算法

//http://www.cppblog.com/oosky/archive/2006/07/06/9486.html

定义

1next[0]= -1  意义:任何串的第一个字符的模式值规定为-1

2next[j]= -1   意义:模式串T中下标为j的字符,如果与首字符相同,且j的前面的1—k个字符与开头的1—k个字符不等(或者相等但T[k]==T[j])(1k<j)。

   如:T=”abCabCad” next[6]=-1,因T[3]=T[6]

3next[j]=k    意义:模式串T中下标为j的字符,如果j的前面k个字符与开头的k个字符相等,且T[j] != T[k] 1k<j)。

         T[0]T[1]T[2]。。。T[k-1]==T[j-k]T[j-k+1]T[j-k+2]…T[j-1]  T[j] != T[k].1k<j;

 ( 4 ) next[j]=0   意义:除(1)(2)(3)的其他情况。

 

举例

01)求T=abcac”的模式函数的值。

     next[0]= -1  根据(1)

     next[1]=0   根据 (4)   因(3)有1<=k<j;不能说,j=1,T[j-1]==T[0]

     next[2]=0   根据 (4)   因(3)有1<=k<j;T[0]=a!=T[1]=b

     next[3]= -1  根据 (2)

     next[4]=1   根据 (3)  T[0]=T[3] T[1]=T[4]

       

下标

0

1

2

3

4

T

a

b

c

a

c

next

-1

0

0

-1

1

T=abcab”将是这样:

下标

0

1

2

3

4

T

a

b

c

a

b

next

-1

0

0

-1

0

为什么T[0]==T[3],还会有next[4]=0, 因为T[1]==T[4], 根据 (3)” T[j] != T[k]”被划入(4)。

02)来个复杂点的,求T=”ababcaabc” 的模式函数的值。

  next[0]= -1    根据(1

        next[1]=0    根据(4)

        next[2]=-1   根据 (2)

  next[3]=0   根据 (3) T[0]=T[2] T[1]=T[3] 被划入(4

  next[4]=2   根据 (3) T[0]T[1]=T[2]T[3] T[2] !=T[4]

  next[5]=-1  根据 (2) 

  next[6]=1   根据 (3) T[0]=T[5] T[1]!=T[6]

  next[7]=0   根据 (3) T[0]=T[6] T[1]=T[7] 被划入(4

  next[8]=2   根据 (3) T[0]T[1]=T[6]T[7] T[2] !=T[8]

 

下标

0

1

2

3

4

5

6

7

8

T

a

b

a

b

c

a

a

b

c

next

-1

0

-1

0

2

-1

1

0

2

只要理解了next[3]=0,而不是=1next[6]=1,而不是= -1next[8]=2,而不是= 0,其他的好象都容易理解。

03)   来个特殊的,求 T=”abCabCad” 的模式函数的值。

下标

0

1

2

3

4

5

6

7

T

a

b

C

a

b

C

a

d

next

-1

0

0

-1

0

0

-1

4

next[5]= 0  根据 (3) T[0]T[1]=T[3]T[4],T[2]==T[5]

next[6]= -1  根据 (2) 虽前面有abC=abC,T[3]==T[6]

next[7]=4   根据 (3) 前面有abCa=abCa, T[4]!=T[7]

T[4]==T[7],即T=” adCadCad”,那么将是这样:next[7]=0, 而不是= 4,因为T[4]==T[7].

下标

0

1

2

3

4

5

6

7

T

a

d

C

a

d

C

a

d

next

-1

0

0

-1

0

0

-1

0

代码

#include <iostream.h>
#include <string.h>
using namespace std;

void getNext(char *p,int *next)
{
    int j,k;
    next[0]=-1;
    j=0;
    k=-1;
    while(j<strlen(p)-1)
    {
        if(k==-1||p[j]==p[k])    //匹配的情况下,p[j]==p[k]
        {
            j++;
            k++;
            next[j]=k;
        }
        else                   //p[j]!=p[k]
            k=next[k];
    }
}

int KMPMatch(char *s,char *p)
{
    int next[100];
    int i,j;
    i=0;
    j=0;
    getNext(p,next);
    while(i<strlen(s))
    {
        if(j==-1||s[i]==p[j])
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];       //消除了指针i的回溯
        }
        if(j==strlen(p))
            return i-strlen(p);
    }
    return -1;
}

int main()//abCabCad
{
     char* text="bababCabCadcaabcaababcbaaaabaaacababcaabc";
     char* pattern="abCab";
   
     cout<< KMPMatch(text, pattern) << endl;

     return 0;
}
原文地址:https://www.cnblogs.com/jeakeven/p/4696424.html