数据结构之串的模式匹配算法(KMP)

这里先给出之前我参考的博客网址,以及参考的书籍是数据结构(严蔚敏)。

参考代码的网址

这里我总结一下我的思路。

先介绍一些基本概念

  1. 主串:这里指的是要匹配的字符串
  2. 模式串:需要在主串中寻找的字符串
  3. KMP匹配算法的重点在于利用模式串自身的重复部分,在匹配中消除那些重复的匹配过程。

下面约定

  1. 字符串和next数组的下标从1开始(人为规定)
  2. i为指向主串的指针,j和k为指向模式串的指针
  3. T代表为模式串数组,S代表主串数组
  4. ==表示左右相等,!=表示左右不相等

next数组的求解

  1. next[1] = 0

我们约定next数组的第一个位置值为0,代表的是模式串的第一个字符失配(也就是连第一个字符都不匹配),这时就从主串当前失配字符的下一个字符开始重新匹配(i增1),模式串当然也是从头开始(j为1)。

  1. 已知next[j] = k

所以:’T[1]…T[k-1]’ == ‘T[j-k+1]…T[j-1]’

这里解释一下:上面的表示模式串的前缀长度为k-1的字符串和从j往前数k-1长度的字符串是相等的。

这时会出现两种情况:

第一种:T[k] == T[j]

所以:’T[1]…T[k]’ == ‘T[j-k+1]…T[j]’
因为:’T[1]…T[k-1]’ == ‘T[j-k+1]…T[j-1]’的时候next[j] = k
所以:’T[1]…T[k]’ == ‘T[j-k+1]…T[j]’的时候next[j+1] = k+1 = next[j] + 1

这里会出现一个问题:如果S[i] != T[j],模式串的第j个字符失配,那么我们就会让S[i]和T[next[j]]进行比较,但是我们这里是第一种情况T[next[j]] == T[j],所以当然也不等于S[i],这就等于做了一次无用功。

所以需要在上面的判断里面再加上一个判断,若T[k+1] == T[j+1],则next[j+1] = next[k+1],否则next[j+1] = k+1

这里举个例子:模式串ABAB,当k指向第一个位置A,j指向第三个位置A,它们俩相等,原本直接赋值next[j+1] = 2但是因为T[k+1] = T[j+1],所以改成next[j+1] = next[k+1] = 1。原本ABAB的next数组为0112,现在变为0101。

第二种:T[k] != T[j]

举个例子:模式串abacdababc,其中k为4,j为9。分别截取出来看就是abac和abab

因为:T[k] != T[j],可以看出abac的前面的a和abab的倒数第二个a相同,就直接就可以从abac的第二个b开始进行比较
所以:这里只需将k改为2也就是k = next[k],让abac第2个和abab最后一个元素对其即可。再次和T[k]比较,若相同则T[j+1] = next[k]+1;否则,再次执行j = next[j]直到j = next[1] = 0为止,这样T[j+1] = next[1]+1 = 1

总结一下就是:

  1. T[1]…T[k]’ == ‘T[j-k+1]…T[j],next[j] = k;
  2. 在1的条件下
    1. T[k]==T[j]且T[k+1] == T[j+1],next[j+1] = next[k+1]
    2. T[k]==T[j]且T[k+1] != T[j+1],next[j+1] = k+1
    3. T[k]!=T[j],k = next[k]
  3. next[1] = 0

因为这些都是推出来的,就像数学的定理,可以因为所以的一步步推出来,所以当成公式或者转化成图像记忆都可以。

综合上述分析,程序思路就可以得出

  1. 将j置为1指向模式串的第一个元素,k为0表示从模式串第一个开始比较,next数组第一位为0
  2. 接下来当然就是一个循环遍历整个模式串
    1. 两个判断条件k是否为0以及T[k]是否等于T[j],若k为0表示又从头开始匹配,则直接将k和j增1,。若T[k]等于T[j]且T[k+1] == T[j+1],表示next[j+1] = next[k+1],若不等于,就按照正常的方法next[j+1] = k+1
      1. 每一次的判断都是为了求得next[j+1]的值,所以这里j需要增1,k增1前面已经讲过了
    2. 否则就是k = next[k]的情况

下面给出改进前和改进后的c语言代码

改进前的代码

void get_next(SString T, int next[]){
    int j = 1;
    int k = 0;
    next[1] = 0;

    while(j<T[0]){
        if(k==0 || T[j]==T[i]){
            j++;
            k++;
            next[j] = k;
        }else{
            k = next[k];
        }
    }

    return;
}

改进后的

void get_nextval(SString T, int nextval[]){
    int j = 1;
    int k = 0;
    next[1] = 0;

    while(j<T[0]){
        if(k==0 || T[k]==T[j]){
            j++;
            k++;
            if(T[k]!=T[j]){
                nextval[j] = k;
            }else{
                nextval[j] = nextval[k];
            }
        }else{
            k = next[k];
        }
    }

    return;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值