KMP算法

KMP算法:D.E.Knuth、J.H.Morris和V.R.Pratt三位前辈发表的一个模式匹配算法,可以避免重复遍历的情况


普通的字符串匹配算法:
串S叫做主串,T是要匹配的串叫做模式串,每次从头开始一位位比较,一旦发现不同,将T串向右移动一位,继续从头开始比较。
在这里插入图片描述
第一次匹配0,1之后发现2号地址不同,然后开始新一轮的比较,将模式串右移一位,继续从头开始比较
在这里插入图片描述
第二次匹配发现1号位置元素不同,继续向后移动一位,知道到四号地址,此时匹配成功。
在这里插入图片描述
暴力破解的代码实现:

public static int violentMatch(String s,String t){
        char[] chs = s.toCharArray();
        char[] cht = t.toCharArray();
        int i=0;
        int j=0;
        while (i<s.length() && j<t.length()){
            if (chs[i] == cht[j]){      //当前字符匹配成功
                i++;
                j++;
            }else{
                i = i-j+1;             //从i-j+1开始继续匹配相当于将匹配串T向右移动一位
                j = 0;
            }
        }
        if (j == t.length()){		//匹配成功返回成功的起始下标
            return i-j;
        }else{
            return -1;
        }
    }

可以发现,对于模式串T来说,“abd"首字母和后面的串”bd"中任意一个字符都不同。也就是说,对于第一次匹配成功的0,1号地址,a字符不可能和1号地址的元素相等,那么第二次匹配其实是不需要的。
在这里插入图片描述
上面这一步是不需要的,直接将T串移动两个位置开始比较,如下图
在这里插入图片描述
我们可以发现如果模式串里相同的字符为0时,每次匹配成功n个字符,下一次就可以向右移动n个位置,那么如果有相同的字符怎么办。
如图,主串是abcababa,模式串是abcabx
在这里插入图片描述
如果5号地址元素不匹配,但是模式串的前缀ab和x之前的后缀ab相等,下一次只需将T移动三个位置继续匹配,此时从五号位置开始比较。也就是说向右移动多少取决于T串的结构,准确来说就是当前字符的前后缀相似度,总结如下
在这里插入图片描述
把T串各个位置向右移动的位置大小也就是j值的变化定义为一个next数组,next[j]的值
1 当j = 1时,不比较
next[j] = max{k, 1 <= k < j 且"t1 … tk-1” = “tj-(k-1) … tj-1”}
0 其他情况

kmp算法流程:

  • 如果j = -1,或者当前字符匹配成功(即chs[i] == cht[j]),都令i++,j++,继续匹配下一个字符;
  • 如果j != -1,且当前字符匹配失败(即chs[i] != cht[j]),则令 i 不变,j = next[j]。此举意味着失配时,模式串T相对于主串S向右移动了j - next [j] 位。
    • 换言之,当匹配失败时,模式串向右移动的位数为:失配字符所在位置 - 失配字符对应的next 值即移动的实际位数为:j - next[j],且此值大于等于1。

KMP实现代码
求next数组值代码如下:

public static int[] getNext(String t,int[] next){
    int length = t.length();
    char[] chars = t.toCharArray();
    next[0] = -1;
    int k = -1;                 //k表示前后缀相等的最大个数
    int j = 0;                  //
    while (j<length-1){
//            chars[k]表示k个相同前后缀增加的下一个元素,chars[j]当前求next值的元素
            //两个值相等,即next+1,如果不相等 相等的元素k就是next[k]的值
        //k=-1 表示刚开始,即j=0,k = 0时 表示没有相等的前后缀
        //规律? 出第一个next[0]=-1外,每一个元素的next数组值只可能是 0或者前一个next+1.
        if (k == -1 || chars[j] == chars[k]){
            //普通求next
            ++k;
            ++j;
            next[j] = k;
            //优化后求nextValue
            /*++k;
            ++j;
            if (chars[j] != chars[k])
                next[j] = k;
            else
                //当chars[j] = chars[next[j]]即chars[k],下一次比较的一定是chars[next[j]] == s[i]
                //肯定是不相等的,因为chars[j] != s[i]
                next[j] = next[k];*/
        }else{
            k = next[k];                //要移动的位数k - next[k]位;
        }
    }
    return next;
}

KMP的实现代码:

public static int kmp(String s,String t,int[] next){
        int i = 0;
        int j = 0;
        char[] chs = s.toCharArray();
        char[] cht = t.toCharArray();
        int sLen = s.length();
        int tLen = t.length();
        while(i<sLen && j<tLen){
            /*
            j = -1考虑的是第一个字符不相等的情况。
            因为next[0] = -1,如果刚开始主串和模式串第一个字符不匹配,那么j=next[0]=-1,
             */
            if (j == -1 || chs[i] == cht[j]){
                i++;
                j++;
            }else{
                j = next[j];            //相当于模式串向右移动的位数为j-next[j];
            }
        }
        if (j == tLen){         //代表成功匹配,j到达了模式串的末位
            return i-j;
        }else{
            return -1;
        }
    }

学习kmp算法的参考

  1. 《大话数据结构》 程杰著
  2. https://www.cnblogs.com/ZuoAndFutureGirl/p/9028287.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值