KMP字符串匹配算法

有字符串 S,字符串 P 表示模式,在 S 中寻找是否有匹配 P 的子字符串

对于字符串 P,要找到一个 next 数组,用于匹配时候下标的跳转,跳转伪代码如下:

if( S[i] != P[j] && j != -1){
    j = next[j];
}else{
    ++j; 
    ++i ;
}

考虑 next 数组中元素的含义可能如下:

(1) 在 P 匹配的过程中,匹配到 S[i] != P[j],那么比较 S[i] 与 P[next[j]],j=next[j] ,不断回溯,直至 P[0] != S[i] ,宣告基于 S[i] 的搜索失败,从 S[i+1]、P[0] 重新开始比较尝试匹配

(2) next 是一个用于回溯的数组,根据 P 字符串自身的特性生成,与 S 无关。

假设 next[i]=-1,此处- 1 表示结束 P 基于 S[i] 的搜索,从判断 S[i+1] 和 P[0] 是否相等开始重新搜索

同时,容易知道,next[0] = -1,

考虑如下情况

1. 考虑有元素相等时,进行递推

若 P[far-near:far-1] == P[0:near-1],此处至少有 far-near>=1,即至少有一个元素相等

1.1 P[near]==P[far]

此时 S[i] != P[far], S[i] != P[near]

因此至少需要一次下标的跳转,所以有 next[far] = next[near]

if P[near]==P[far]
    far++
    near++
    if P[far]==P[near]
        next[far]=next[near]

注意,上述代码中 next[far]=next[near] 只有一次回溯,并不完整

后续应在(2)中进行回溯 near = next[near]

1.2 P[near]!=P[far]

此时 S[i] 的值可能与 P[near] 相等,至少有 near 个元素,因此 next[far] = near

if P[near]==P[far]
    far++
    near++
    if P[far]!=P[near]
        next[far]=near

考虑 (1.2) 和 (1.3) ,得到

if P[far] == P[near]
    far++
    near++
    if P[far]==P[near]
        next[far]=next[near]
    else
        next[far]=near

1.3 考虑初始值

由 (1.2) 中看到 next[far]=near,next[0] = -1 ,则 near 可能为 -1,near应当初始化为 -1

考虑到 next[0]=-1 是已经确定的,而P[1]对应的next[1[需要考虑到P[0]的值,即当 near=0 时,far 值为 1

if near == -1
    far=1
    near=0
    if P[far]==P[near]
        next[far]=next[near]
    else
        next[far]=near

综合 (1.1) (1.2) 时

if near == -1 || P[far] == P[near]
    far++
    near++
    if P[far]==P[near]
        next[far]=next[near]
    else
        next[far]=near

由上可知,far 初始化的值为 0

2 考虑元素不等时进行回溯

如果 P[far]!=P[near] , 则 near = next[near] 不断回溯

直至最后 near 为-1,或者 P[far] == P[near],即 (1) 中两个判断条件

near == -1 || P[far] == P[near]

完整代码如下:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4 #include <stdbool.h>
  5 
  6 typedef struct Matchinfo{
  7     unsigned int size;
  8     unsigned int matchpos[];
  9 } MatchStruct;
 10 
 11 MatchStruct * bruteForce(const char* S,const char* P){
 12     int lenS = strlen(S),lenP = strlen(P);
 13     unsigned int matcharr[lenS];
 14     int pos = -1;
 15     int i = 0,j = 0;
 16     for(i = 0;i <= lenS - lenP; i++){
 17         bool flag = true;
 18         for(j = 0 ; j < lenP ; ++j){
 19             if(S[i+j] != P[j]){
 20                 flag = false;
 21                 break;
 22             }
 23         }
 24         if(flag){
 25             pos++;   
 26             matcharr[pos] = i;
 27         }
 28     }
 29     if(pos == -1){
 30         return NULL;
 31     }else{
 32         //unsigned int* matchpos = (unsigned int *)malloc(sizeof(unsigned int)*(pos+1));
 33         MatchStruct * result = (MatchStruct*)malloc(sizeof(MatchStruct) + sizeof(unsigned int)*(pos+1));
 34         int start ;
 35         for(start = 0; start <= pos;++start){
 36             result->matchpos[start] = matcharr[start];
 37         }
 38         result->size = pos+1;
 39         return result;
 40     }
 41 }
 42 
 43 void GetNextArray(const char* P,int next[]){
 44     int size = strlen(P),far = 0,near = -1;
 45     next[0] = -1;
 46     int start = 0;
 47     while(far < size){
 48         if(near==-1 || P[near]==P[far]){
 49             near++;
 50             far++;
 51             if(P[near]==P[far])
 52                 next[far] = next[near];
 53             else
 54                 next[far] = near;
 55             
 56         }else
 57         {
 58             near = next[near];
 59         }
 60     }
 61 }
 62 
 63 MatchStruct * KMPSerch(const char* S,const char* P){
 64     int lenS = strlen(S),lenP = strlen(P);
 65     unsigned int matcharr[lenS];
 66     int pos = -1;
 67     int i = 0,j = 0;
 68     
 69     int next[lenP];
 70     GetNextArray(P,next);
 71 
 72     while(i <= (lenS - lenP)){
 73         //printf( "%d %d %c \n",i,j,S[i]);
 74         if( S[i] != P[j] && j != -1){
 75             j = next[j];
 76         }else{
 77             if (j == (lenP-1)){
 78                 pos++,matcharr[pos] = i-lenP+1,j=0;
 79             }else{
 80                ++j; 
 81             }
 82             ++i ;
 83         }
 84     }
 85 
 86     if(pos == -1){
 87         return NULL;
 88     }else{
 89         MatchStruct * result = (MatchStruct*)malloc(sizeof(MatchStruct) + sizeof(unsigned int)*(pos+1));
 90         int start ;
 91         for(start = 0; start <= pos;++start){
 92             result->matchpos[start] = matcharr[start];
 93         }
 94         result->size = pos+1;
 95         return result;
 96     }
 97 }
 98 
 99 int main(void){
100     
101     char *S="xwegxwegwaabaac",*P= "xweg";
102     //MatchStruct* match = bruteForce(S,P);
103     MatchStruct* match = KMPSerch(S,P);
104     if(match == NULL)
105         printf("No substring match!\n");
106     else{
107         printf("Substring match!\n");
108         int start = 0;
109         for(;start < match->size;start++){
110             printf("%d\n",match->matchpos[start]);
111         }
112         printf("Search Finished!\n");
113     }
114 
115     return 0;
116 }
View Code

 

 

posted @ 2021-12-03 22:10  星座北斗  阅读(114)  评论(0)    收藏  举报