KMP算法

110 阅读3分钟

KMP算法介绍

KMP算法是三位学者在 Brute-Force算法的基础上同时提出的模式匹配的改进算法。Brute- Force算法在模式串中有 多个字符和主串中的若干个连续字符比较都相等,但最后一个字符比较不相等时,主串的比较位置需要回退。KMP算法在 上述情况下,主串位置不需要回退,从而可以大大提高效率。

KMP算法应用-字符串匹配问题

  1. 有一个字符串 str1= "BBC ABCDAB ABCDABCDABDE",和一个子串 str2="ABCDABD"
  2. 现在要判断 str1 是否含有 str2, 如果存在,就返回第一次出现的位置, 如果没有,则返回-1
  3. 要求:使用 KMP 算法完成判断,不能使用简单的暴力匹配算法。

例如: 有一个字符串 Str1 = “BBC ABCDAB ABCDABCDABDE”,判断,里面是否包含另一个字符串 Str2 = “ABCDABD”?
1.首先,用 Str1 的第一个字符和 Str2 的第一个字符去比较,不符合,关键词向后移动一位

图片.png

  1. 重复第一步,还是不符合,再后移

图片.png

  1. 一直重复,直到 Str1 有一个字符与 Str2 的第一个字符符合为止

图片.png

  1. 接着比较字符串和搜索词的下一个字符,还是符合。

图片.png

5.遇到 Str1 有一个字符与 Str2 对应的字符不符合。

图片.png

6.这时候,想到的是继续遍历 Str1 的下一个字符,重复第 1 步。(其实是很不明智的,因为此时 BCD 已经比较过了, 没有必要再做重复的工作,一个基本事实是,当空格与 D 不匹配时,你其实知道前面六个字符是”ABCDAB”。KMP 算法的想法是,设法利用这个已知信息,不要把”搜索位置”移回已经比较过的位置,继续把它向后移,这 样就提高了效率。)

图片.png

8.已知空格与 D 不匹配时,前面六个字符”ABCDAB”是匹配的。查表可知,最后一个匹配字符 B 对应的”部分匹配值”为 2,因此按照下面的公式算出向后移动的位数: 移动位数 = 已匹配的字符数 - 对应的部分匹配值因为 6 - 2 等于 4,所以将搜索词向后移动 4 位。
9.因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为 2(”AB”),对应的”部分匹配值” 为 0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移 2 位。

图片.png

10.因为空格与 A 不匹配,继续后移一位。

图片.png

11.逐位比较,直到发现 C 与 D 不匹配。于是,移动位数 = 6 - 2,继续将搜索词向后移动 4 位。

图片.png

12.逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),
移动位数 = 7 - 0,再将搜索词向后移动 7 位,这里就不再重复了。

图片.png

public class KmpTest {
    public static void main(String[] args) {
        System.out.println(new KmpSulution().strStr("BBC ABCDAB ABCDABCDABDE", "ABCDABD"));
    }

    public int strStr(String haystack, String needle) {
        int i = 0;
        int j = 0;
        int[] next = getNext(needle);
        while (i < haystack.length() && j < needle.length()) {
            if (j == -1 || haystack.charAt(i) == needle.charAt(j)) {
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
        if (j == needle.length()) {
            return i - j;
        } else {
            return -1;
        }
    }

    public int[] getNext(String sub) {
        int[] next = new int[sub.length() + 1];
        int i = 0;
        int j = -1;
        while (i < sub.length()) {
            if (j == -1 || sub.charAt(i) == sub.charAt(j)) {
                next[++i] = ++j;
            } else {
                j = next[j];
            }
        }
        return next;
    }
}