如何理解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

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

代码如下:

扫描二维码关注公众号,回复: 9601464 查看本文章
// 构造 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));
}

猜你喜欢

转载自www.cnblogs.com/zhengyu-ahu/p/12421217.html