字符串之KMP算法

KMP算法的精髓在于得到PMT(Partial Match Table)表格,有些同学使用next表示,这是因为在实际使用中使用的是pmt左移一个单位之后的表格

要想得到PMT表格,首先需要知道以下几个概念:

子串:不包含字符串本身

前缀:字符串子串集合中以首字符开头的子串

后缀:字符串子串集合中以尾字符结束的子串

PMT:前缀集合和后缀集合中相同字符串的最大长度

举例:

A B C D A B D

A:无前缀和后缀

AB:前缀:A

        后缀:B

        前缀和后缀无相同字符串,则最大长度为0

ABC:前缀:A、AB

           后缀:BC、C

           前缀和后缀无相同字符串,则最大长度为0

ABCD:前缀:A、AB、ABC

              后缀:BCD、CD、D

              前缀和后缀无相同字符串,则最大长度为0

             (有没有发现ABCD的前缀和ABC的前缀有一种关系,同样ABCD的后缀和ABC的后缀也有一种关系)

ABCDA:前缀:A、AB、ABC、ABCD

                后缀:BCDA、CDA、DA、A

                前缀和后缀共有字符串为A,则最大长度为1

ABCDAB:前缀:A、AB、ABC、ABCD、ABCDA

                  后缀:BCDAB、CDAB、DAB、AB、B

                  前缀和后缀相同字符串为AB、B,则最大长度为2

ABCDABD:前缀:A、AB、ABC、ABCD、ABCDA、ABCDAB

                     后缀:BCDABD、CDABD、DABD、ABD、BD、D

                     前缀和后缀无相同字符串,则最大长度为0

当比较到蓝色部分时,dst的ABCDAB的PMT值为2,即最前的两个字符与最后面的两个字符相等

即可以确定src的中标黄色的部分和dst中标黄色部分相等,因此src标黄色的部分和dst标绿色的部分相等,

所以下次src标蓝色的部分和dst标红色的部分开始比较

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

/*
 * 前缀:字符串子串集合中以首字符开头的子串
 * 后缀:字符串子串集合中以尾字符结束的子串
 * pmt中的值为前缀和后缀中共有的最大长度的值
 */
void GetPmt(char *string, int *pmt)
{
    int i;
    int commLengthId = 0;
    int length = strlen(string);
    pmt[0] = 0;
    for (i = 1; i < length; i++) {
        while ((commLengthId > 0) && (string[i] != string[commLengthId])) {
            commLengthId = pmt[commLengthId - 1];
        }

        if (string[i] == string[commLengthId]) {
            commLengthId++;
        }

        pmt[i] = commLengthId;
    }
}

void GetNext(char *string, int *next)
{
    int i;
    int commLengthId = 0;
    int length = strlen(string);
    next[0] = 0;
    next[1] = 0;
    for (i = 1; i < length; i++) {
        while ((commLengthId > 0) && (string[i] != string[commLengthId])) {
            commLengthId = next[commLengthId];
        }

        if (string[i] == string[commLengthId]) {
            commLengthId++;
        }

        next[i + 1] = commLengthId;
    }
}

/* id:  0 1 2 3 4 5 6 7 8 9
 * src: a b c a b a b a e f
 * dst:       a b a e f
 * 当比较到第6位的时候,b 和 e不相等,dst中e前三位为aba,最大pmt值为1,
 * 意味着b之前有一位至少是已经能对其的,下一次比较从dst中的b开始
 * src: a b c a b a b a e f
 * dst:           a b a e f
 * 返回找到目标字符串的原字符串起始下标
 */

int KmpPro(char *src, char *dst)
{
    int i = 0;
    int j = 0;
    int srcLength = strlen(src);
    int dstLength = strlen(dst);
    int *next = (int *)malloc((dstLength + 1) * sizeof(int));
    GetNext(dst, next);

    while ((i < srcLength) && (j < dstLength)) {
        if (src[i] == dst[j]) {
            i++;
            j++;
        } else if (j == 0) {
            i++;
        } else {
            j = next[j];
        }
    }
    free(next);
    next = NULL;
    if (j == dstLength) {
        return (i - j);
    }
    return -1;
}


int main()
{
    freopen("test/1.txt", "r", stdin);
    char src[100];
    char dst[100];

    scanf("%s", src);
    scanf("%s", dst);
    printf("%d\n", KmpPro(src, dst));

    system("pause");
    return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值