字符串匹配算法(KMP)

KMP算法JAVA代码实现

“KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。

KMP思路:
1.先得到子串的部分匹配表;
2.使用部分匹配表完成KMP算法;

1.部分匹配表,
把子串的每一个前缀当成一个独立的字符串,对于每一个字符串来找出它的最长公共前后缀的长度,组成一张表,称为部分匹配表.
我们用一个字符串(ABABCABAA)举个例子:字符串匹配算法(KMP)_第1张图片
用 ABABCABA 举个例子吧:
它的前缀有:
A
AB
ABA
ABAB
ABABC
ABABCA
ABABCAB
它的后缀有:
A
BA
ABA
CABA
BCABA
ABCABA
BABCABA
所以它的公共前后缀是ABA,长度为3,所以部分匹配表对应的值就为3.

对应的代码实现:

 //获取到一个字符串(子串)的部分匹配值的表
    public static int[] kmpNext(String dest) {
        //先将字符串转化为char数组;
        char pattern[] = dest.toCharArray();
        int n = pattern.length;//n代表数组的长度
        //创建一个部分匹配值的表,长度和传入的字符串的长度相同
        int prefix[] = new int[dest.length()];
        //默认pattern[0]=0;
        prefix[0] = 0;
        int len = 0;
        int i = 1;
        while (i < n) {
            //System.out.println(i + "|||||" + len);
            if (pattern[i] == pattern[len]) {
                len++;
                prefix[i] = len;
                i++;
            } else {
                if (len > 0) {
                    len = prefix[len - 1];
                } else {
                    prefix[i] = 0;
                    i++;
                }
            }
        }
        return prefix;
    }

然后将得到的表向后移动一位,让prefix[0] = -1;

 public static void move_pattern(int prefix[]) {
        for (int i = prefix.length - 1; i >= 1; i--) {
            prefix[i] = prefix[i - 1];
        }
        prefix[0] = -1;
    }

写一个main方法进行测试:

 public static void main(String[] args) {
        String a = "ABABCABAA";
        int[] prefex = kmpNext(a);
        System.out.println("原部分匹配表:" + Arrays.toString(prefex));
        move_pattern(prefex);
        System.out.println("移动后的部分匹配表:" + Arrays.toString(prefex));
    }

结果如下:
字符串匹配算法(KMP)_第2张图片
2.使用部分匹配表完成KMP算法:

 //haystack(父串) needle(子串)
    public static int kmpSearch(String haystack, String needle) {
        char father[] = haystack.toCharArray();
        char son[] = needle.toCharArray();
        int prefex[] = kmpNext(needle);
        move_pattern(prefex);
        int m = father.length; //m为父串的长度
        int n = prefex.length;  //n为子串的长度
        int i = 0;
        int j = 0;
        while (i < m) {
            if (j == n - 1 && father[i] == son[j]) {
                return i - j;
                //如果继续匹配的话应该要:
                // j = prefex[j];
            }
            if (father[i] == son[j]) {
                i++;
                j++;
            } else {
                j = prefex[j];
                if (j == -1) {
                    i++;
                    j++;
                }
            }
        }
        return -1; //如果没有匹配到返回-1
    }

全部代码:

public class KMP {
    /*
    KMP思路:
    1.先得到子串的部分匹配表;
    2.使用部分匹配表完成KMP算法;
     */

    //获取到一个字符串(子串)的部分匹配值的表
    public static int[] kmpNext(String dest) {
        //先将字符串转化为char数组;
        char pattern[] = dest.toCharArray();
        int n = pattern.length;//n代表数组的长度
        //创建一个部分匹配值的表,长度和传入的字符串的长度相同
        int prefix[] = new int[dest.length()];
        //默认pattern[0]=0;
        prefix[0] = 0;
        int len = 0;
        int i = 1;
        while (i < n) {
            //System.out.println(i + "|||||" + len);
            if (pattern[i] == pattern[len]) {
                len++;
                prefix[i] = len;
                i++;
            } else {
                if (len > 0) {
                    len = prefix[len - 1];
                } else {
                    prefix[i] = 0;
                    i++;
                }
            }
        }
        return prefix;
    }

    public static void move_pattern(int prefix[]) {
        for (int i = prefix.length - 1; i >= 1; i--) {
            prefix[i] = prefix[i - 1];
        }
        prefix[0] = -1;
    }

    //haystack(父串) needle(子串)
    public static int kmpSearch(String haystack, String needle) {
        char father[] = haystack.toCharArray();
        char son[] = needle.toCharArray();
        int prefex[] = kmpNext(needle);
        move_pattern(prefex);
        int m = father.length; //m为父串的长度
        int n = prefex.length;  //n为子串的长度
        int i = 0;
        int j = 0;
        while (i < m) {
            System.out.println(i + "||||" + j);
            if (j == n - 1 && father[i] == son[j]) {
                return i - j;
                //如果继续匹配的话应该要:
                // j = prefex[j];
            }
            if (father[i] == son[j]) {
                i++;
                j++;
            } else {
                j = prefex[j];
                if (j == -1) {
                    i++;
                    j++;
                }
            }
        }
        return -1;
    }

    
}

力扣第28题测试结果如下:
字符串匹配算法(KMP)_第3张图片

你可能感兴趣的:(数据结构与算法,算法,leetcode,哈希算法,java,字符串)