zoukankan      html  css  js  c++  java
  • 如何理解KMP算法

    KMP算法的核心思想:设法让主串不回退,让子串回退的更少

    整个过程分为两步:

    1. 为子串建立一张最长匹配长度的表
    2. 利用这种表的索引对主串实现匹配判断

    测试样本:

    主串:"fsaabababzabababaaba"
    子串:"abababzabababa"
    

    第一步

    规则:对每个子字符串 [0...i],算出其「相匹配的前缀与后缀中,最长的字符串的长度」。

    例如:

    当i=5时,对子字符串ababab来说
    前缀:a、ab、aba、abab、ababa
    后缀:b、ab、bab、abab、babab
    此时最长匹配长度为4,匹配项为abab,同时其中同时看到另外一个匹配项ab
    

    了解了这张表的来历,那么应该怎样简化运算来求解最后一位a的最长匹配长度呢?

    当i=12时,对子字符串 abababzababab 来说,
    前缀:a, ab, aba, abab, ababa, ababab, abababz, ...
    后缀:b, ab, bab, abab, babab, ababab, zababab, ...
    

    子字符串abababzababab前缀后缀最大匹配了6个(ababab),那次大匹配了多少呢?容易看出次大匹配了4个(abab),更仔细地观察可以发现,次大匹配的前缀后缀只可能在 ababab 中,所以次大匹配数就是 ababab 的最大匹配数!第三大同理,可得abab的最大匹配数,查表可得该值为2。

    ?处找最大匹配发现结尾不是z,所以就不能直接6+1=7,应该找次大匹配,发现刚好对上abab后的字符a,所以?为4+1=5,匹配项为abab

    到此为止,第一步已经完成了

    代码如下:

    // 构造 pattern 的最大匹配数表
    public static int[] calculateMaxMatchLengths(String pattern) {
        int[] maxMatchLengths = new int[pattern.length()];
        int maxLength = 0;
        for (int i = 1; i < pattern.length(); i++) {
            while (maxLength > 0 && pattern.charAt(maxLength) != pattern.charAt(i)) {
                maxLength = maxMatchLengths[maxLength - 1];
            }
            if (pattern.charAt(i) == pattern.charAt(maxLength)) {
                maxLength++;
            }
            maxMatchLengths[i] = maxLength;
        }
        return maxMatchLengths;
    }
    

    第二步

    既然子串的表已经建立,那么下一步就是对主串使用了

    首先,主串是不回退的,所以这里用for循环实现从头到尾的遍历,同时对应寻找子串的最大匹配

    在循环内部分三种情况执行:

    1. 部分匹配成功,指针i和count往后移动
    2. 匹配失败,改变count,寻找次大匹配
    3. 完全匹配成功,记录位置
    public static List<Integer> KMP_search(String text,String pattern){
        List<Integer> positions=new ArrayList<>();
        int[] maxMatchLengths = calculateMaxMatchLengths(pattern);
        int count = 0;
        for (int i = 0; i < text.length(); i++) {
            // charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1。
            while (count > 0 && pattern.charAt(count) != text.charAt(i)) {
                count = maxMatchLengths[count - 1];
            }
            if (pattern.charAt(count) == text.charAt(i)) {
                count++;
            }
            if (count == pattern.length()) {
                // 完全匹配成功时
                positions.add(i - pattern.length() + 1);
                count = maxMatchLengths[count - 1];
            }
        }
        return positions;
    }
    

    好了,到此为止KMP算法的核心已经写完了,下面开始测试:

    public static void main(String[] args) {
        String str= "abababzabababa";
        String text="abbabababzabababac";
        int[] a=Test.calculateMaxMatchLengths(str);
        for (int s:a) {
            System.out.println(s);
        }
        System.out.println(Test.KMP_search(text,str));
    }
    

  • 相关阅读:
    Go 语言简介(下)— 特性
    Array.length vs Array.prototype.length
    【转】javascript Object使用Array的方法
    【转】大话程序猿眼里的高并发架构
    【转】The magic behind array length property
    【转】Build Your own Simplified AngularJS in 200 Lines of JavaScript
    【转】在 2016 年做 PHP 开发是一种什么样的体验?(一)
    【转】大话程序猿眼里的高并发
    php通过token验证表单重复提交
    windows 杀进程软件
  • 原文地址:https://www.cnblogs.com/zhengyu-ahu/p/12421217.html
Copyright © 2011-2022 走看看