KMP字符串查找

KMP算法

KMP字符串查找算法的目的是减少不必要的比较次数,举个简单的例子,从字符串A:”abcdeabcdfg”中查找字符串B:”abcdf”。

使用普通的查找法查找字符串的步骤是这样的:

先拿A[0:4]分别与B对应位置的字母比较,如果不相等则拿A[1:5]与B比较,依次类推,直到结束。

而KMP算法先分析要朝朝的字符串,以B为例,由于a与后面的四个字节都不相同,而在比较A[0:4]与B时在A[4]处发生不匹配现象,所以,再拿A[1:5]、A[2:6]、A[3:7]分别与B比较是没有意义的,直接比较A[4:8]与B即可。这样就省掉了几次循环。

KMP算法在分析检索关键词时需要考虑的问题还有很多,这里就不一一分析了,下面是C实现的代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void getNext( char* keyword, char* next){
    int i = 0, j = -1;
    next[0] = -1;

    int end_pos = strlen(keyword)-1;
    while(i < end_pos){
        if(j == -1 || keyword[j] == keyword[i]){
            ++ i;
            ++ j;
            next[i] = j;
        }
        else{
            j = next[j];
        }
    }
}

int find( char *string, char *keyword){
    int i = 0, j = 0, len = strlen(string), klen = strlen(keyword);
    char *next = (char*)malloc( strlen(keyword));
    getNext(keyword, next);

    while(i < len && j < klen){
        if(j == -1 || string[i] == keyword[j]){
            ++i;
            ++j;
        }
        else{
            j = next[j];
        }
    }
    free(next);
    if(j == klen){
        return i-klen;
    }
    else{
        return -1;
    }
}

int main(){
    char *keyword = "abcdef";
    char next[strlen(keyword)];
    getNext(keyword, next);

    int i;
    int len = strlen(keyword);
    printf("next:" );
    for(i = 0; i != len; ++i){
        printf("%d" , next[i]);
    }
    printf("\n" );

    int pos = find("adadababcdefcf", keyword);

    printf(" pos = %d\n", pos);

    return 0;
}

优化KMP算法

为了更大限度的减少重复匹配次数,还可以对KMP算法进行优化,优化步骤非常简单,下面是优化后的代码,新增的代码后都标有line开头的注释:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void getNext( char* keyword, char* next){
    int i = 0, j = -1;
    next[0] = -1;

    int end_pos = strlen(keyword)-1;
    while(i < end_pos){
        if(j == -1 || keyword[j] == keyword[i]){
            ++ i;
            ++ j;
            if(keyword[j] == keyword[i]){            // line 1
                next[i] = next[j];                   // line 2
            }                                        // line 3
            else{                                    // line 4
                next[i] = j;                         // line 5
            }                                        // line 6
        }
        else{
            j = next[j];
        }
    }
}

int find( char *string, char *keyword){
    int i = 0, j = 0, len = strlen(string), klen = strlen(keyword);
    char *next = (char*)malloc( strlen(keyword));
    getNext(keyword, next);

    while(i < len && j < klen){
        if(j == -1 || string[i] == keyword[j]){
            ++i;
            ++j;
        }
        else{
            j = next[j];
        }
    }
    free(next);
    if(j == klen){
        return i-klen;
    }
    else{
        return -1;
    }
}

int main(){
    char *keyword = "ababa";
    char next[strlen(keyword)];
    getNext(keyword, next);

    int i;
    int len = strlen(keyword);
    printf("next:" );
    for(i = 0; i != len; ++i){
        printf("%d" , next[i]);
    }
    printf("\n" );

    int pos = find("abasbabababababababababab", keyword);

    printf("pos = %d\n", pos);

    return 0;
}

其实,理解起来也比较简单,对于检索关键词K:”ababa”这种情况,假设外层循环计数变量为i,如果在K[0]处发生不匹配,则下次循环前i应该自加,重新与K进行比较,而如果在K[1]处发生不匹配时,则下次循环前,i无需自加,直接与K进行比较,但是如果在K[2]处发生不匹配时,则下次循环前,i应该自加然后与K进行匹配,因为K[2]跟K[0]相等,K[2]与之不匹配则K[0]也与之不匹配,所以i需要自加,从后一个元素开始比较。

本文链接:
http://blog.csdn.net/girlkoo/article/details/17435557

本文作者:girlkoo

    原文作者:KMP算法
    原文地址: https://blog.csdn.net/girlkoo/article/details/17435557
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞