KMP 算法浅读

KMP算法

本文通过解读Leetcode.28 问题来大概解释KMP算法

例如一个主字符串 “aabaabaaf” 需要匹配一个子字符串 “aabaaf”

  • 前缀与后缀
    以上文子字符串(aabaaf)为例,前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串,例如a,aa,aab,aaba,aabaa都是属于前缀,后缀也由此可知,不包括第一个字符并以最后一个字符结尾的连续子串,如 f,af,aaf,baaf,abaaf都是后缀,那么此时就有一个关键点,就是求前缀表。

  • 前缀表
    求前缀表的关键就在于找到最长相等前后缀,如a最长相等前后缀长度为0,aa为1,aaa为2等等。
    前缀表的作用就是告诉我们上次已经匹配好的位置,如果这次匹配不成功,就通过查找此表找到上一次字符匹配的位置,那么如何计算前缀表。

  • 计算前缀表
    例如字符串aabaaf,从头开始遍历,第一个子串 a 的最长相等前后缀为0,aa 为 1,aab 为 0,aaba 为 1,aabaa 为 2,aabaaf 为 0。所以求得的最长相等前后缀其实就是前缀表的数值。如果看不懂文字可以看以下代码(Java),前缀表一般用next数组表示,s为子字符串(如上的aabaaf),代码块省略了next数组初始化

/**
next 数组初始化, int[] next = new int[s.length()];
**/
public void getNext(int[] next,String s){
        int j = 0;
        for(int i = 1;i<s.length();i++){
            while(j>0 && s.charAt(i) != s.charAt(j)){
                j = next[j-1];
            }
            if(s.charAt(i) == s.charAt(j)){
                j++;
            }
            next[i] = j;
        }
    }

j = next[j-1] 这一步就是查前一个匹配的位置
由上代码块可以自己尝试模拟求取 aabaaf 前缀表的过程。此时aabaaf对应的数组就是[0,1,0,1,2,0]。

求完前缀表基本上就可以解出这道题了。以下放出本题总代码。最后的主字符串和子字符串的匹配又类似于求前缀表。

class Solution {
    //前缀表(不减一)Java实现
    public int strStr(String haystack, String needle) {
        if (needle.length() == 0) return 0;
        int[] next = new int[needle.length()];
        getNext(next, needle);

        int j = 0;
        for (int i = 0; i < haystack.length(); i++) {
            while (j > 0 && needle.charAt(j) != haystack.charAt(i)) 
                j = next[j - 1];//跳转到前一个匹配的位置
            if (needle.charAt(j) == haystack.charAt(i)) 
                j++;
            if (j == needle.length()) 
                return i - needle.length() + 1;//返回的是第一次出现子字符串的第一个字符的下标
        }
        return -1;

    }
    
    private void getNext(int[] next, String s) {
        int j = 0;
        next[0] = 0;
        for (int i = 1; i < s.length(); i++) {
            while (j > 0 && s.charAt(j) != s.charAt(i)) 
                j = next[j - 1];
            if (s.charAt(j) == s.charAt(i)) 
                j++;
            next[i] = j; 
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值