串的模式匹配算法(KMP算法)

说明:设主串为S,模式串为T,i表示主串元素的下标,j表示模式元素的下标,k是next( )函数找出来模式的下标
为了实现这些算法,都需要对串进行“定长顺序存储表示”

一、子串位置的定位函数(回溯法)

**
1. 算法思想:
主串的第pos个位置,和模式的第1个位置开始比较,如果相等,则继续比较后续字符;若不等,则 i 指针跑到pos的下一个位置上,j 指针继续从第1个位置开始比较……直到找到对应的子串。
在这里插入图片描述
代码如下:

int Index(SString S, SString T, int pos) {
    int i = pos;    int j = 1;
    while(i <= S[0] || j <= T[0]) {
        if(S[i] == T[j]) {    //如果相等,则继续比较后续字符
            i++;    j++
        }else {
            i = i-j+2;    //不相等,则 i 指针往右移动一格,j 指针继续从第一项开始
            j = 1;
        }
    }
    if(j > T(0))    return i-T(0);    //返回子串在主串的第一个匹配字符的位置
    else return 0;    //不匹配,返回0
}
  1. 算法分析
    若设n和m分别是主串和模式的长度,则一般情况下,该算法的时间复杂度为O(n + m)
    但是在最坏情况下,它的复杂度为O(n * m)【主串和模式的前面字符都匹配,只有最后一个字符不一样】
    于是我们想找一种更好、更稳定的算法……(前方高能!!)

二、KMP算法(非回溯法)

  1. 对第一种方法的改进
    改进:在这种算法模式下,我们不需要对 i 指针进行回溯,我们只用向右移动模式串就行了。
    我们需要解决一个问题:模式串要移动多远,即主串中第 i 个字符应与模式的第几个字符比较?
    **
    2. 算法思想:
    假设移动模式串后,模式的第 k ( k < j ) 个字符与主串的第 i 个字符比较,则必定满足:
    主串 i 指针之前的 k-1 个字符,和模式从头开始的 k-1 个字符匹配!
    如果我们只看模式串的话,则会发现:如果模式的第 j 个字符发生失配,那么必定满足:
    模式 j 指针之前的 k-1 个字符,和模式从头开始的 k-1 个字符匹配!

由此,我们产生了方法——当匹配过程中主串第 i 个字符和模式第 j 个字符不匹配时,只需把模式移动至它的第 k 项和主串第 i 个字符对齐就行了!
然后从这儿开始比较 k 和 i 配不配:如果匹配,那么往下继续比较;如果不匹配,那么再一次移动模式串(方法和加粗部分完全相同)

既然模式的每一项都有不匹配的可能,那么我们用一个数组next[ ],来写出每一项不匹配的时候所对应的 k 值(next[ j ] = k【很重要!】)。
那么如何求出每一项的k值呢?有如下规则:
1. 规定:next[1] = 0;
2. 比较第 k 和第 j 个字符是否匹配——
2.1. 如果匹配(T[ k ] == T[ j ]),则next[ j + 1 ] = next[ j ] + 1;
2.2. 如果不匹配(T[ k ] != T[ j ])再看k的next[ k ]
2.2.1. 若next[ k ] == T[ j ],则next[ j + 1 ] = next[ k ] + 1;
2.2.2. 若next[ k ] != T[ j ],那么继续找next[ k ]这一项的 k 值,再比较和第 j 个字符是否匹配,如果不匹配就一直找下去……
最后,如果你幸运找到了匹配项,则next[ j + 1 ]就是你找到的那一项的项数 + 1
如果不幸一直没找到,那么next[ j + 1 ]就等于1。

KMP算法图示
来看看代码:

    void get_next(SString T, int next[]) {
    int i = 1;    //用于遍历模式串
    next[1] = 0;    //规定
    int j = 0;    //next数组里面的数
    while(i < T(0)) {
        if(j == 0 || T[i] = T[j]) {    //一旦第k项和第j项匹配,那么next数组的j+1项的值就是你找到的那一项的项数 + 1
            i++;    j++;
            next[i] = j;
        }else {
            j = next[j];    //如果不匹配,就找第j项对应的k值
        }
}

现在,我们就有了模式串每一项所对应的k值了!

最后,我们来看看现在定位函数的实现:

int Index(SString S, SString T, int pos) {
    int i = pos;    int j = 1;
    while(i <= S[0] || j <= T[0]) {
        if(j == 0 || S[i] = T[j]) {    //匹配,则比较下一个字符
            i++;    j++
        }else {
            j = next[j];    //不匹配,移动模式串到下一个k处,再比较是否匹配
        }
    }
    if(j > T(0))    return i-T(0);    //返回子串在主串的第一个匹配字符的位置
    else return 0;    //不匹配,返回0
}

思路来源于严奶奶的《数据结构》

最后感谢https://blog.csdn.net/qq_37969433/article/details/82947411
里面的动图来自这篇博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值