JAVA数据结构与算法:KMP

摘要

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

简介

​ 字符串的匹配是对字符串的基本操作,最直接的方式是进行全遍历搜索的回溯法,但是其复杂度较高,而随着人们对于匹配效率的追求与匹配优化,其匹配的复杂度逐渐降低。
​ 在这个过程中最重要的当属于KMP了,他将原有的算法复杂度降低到了O(n+m) 其中n,m是指两个字符串的长度。

详解

最直接的回溯法

​ 从字符串左边开始匹配,如果遇到相同的sc与j同事进行+1,直到全部遍历完p的时候进行返回下标,如果匹配过程有不一样的sc进行回溯到i。

public class Match_kmp {
    public static void main(String[] args) {
        System.out.println(indexOf("aaavbvdd","vbv"));
    }
    private static int indexOf(String s,String p){
        int i = 0;
        int sc = i;
        int j = 0;
        while (sc<s.length()){
            if(s.charAt(sc)==p.charAt(j)){
                j++;
                sc++;
                if(j==p.length()){
                    return i;
                }
            }else
            {
                i++;
                sc=i;
                j=0;
            }
        }
        return -1;
    }
}

其结果是返回第一个匹配的下标i

  • 这种求解的方法复杂度达到O(n*m),下面介绍KMP算法.

KMP匹配

图解

1571297220130

①从左到右进行匹配,此时 i =0,j=0;如果相同 j++,i++,如果不相同 j进行回溯

②匹配到最后一个不相同的时候进行回溯,如果暴力进行匹配的时候 i ++,令j=0;
但是我们可以借助之前已经比较的字符串T,进行回溯。这样比较的时候就会减少复杂度。j回溯的大小取决于匹配字符串T的next[数组]。Next数组的求解放到下面说.next={0,0,0,0,2,2}
= n e x t [ j ] 右移的大小=以经匹配的字符串-next[j]
所以next[5]=2,右移的大小=5-2,此时j也进行回溯,
j = n e x t [ j 1 ] j = next[j-1]
③以此类推,此时j=2;进行比较 ,不相同,进行回溯 j = 2-next[j-1]=2…

KMP

public class MatchKMP {
    public static void main(String[] args) {
        int ne[] =getNext("abcdabd");
        int res = kmp("ssdfgasdbababa","bababa",ne);
        System.out.println(res);
    }
    private static int kmp(String s,String t,int[] next){
        for (int i = 0,j=0;i<s.length();i++){
            while(j>0&&s.charAt(i)!=t.charAt(j)){
                j=next[j-1];
            }if(s.charAt(i)==t.charAt(j)){
                j++;
            }
            if(j==t.length()){
                return i-j+1;
            }
        }
        return 0;
    }
    private static int[] getNext(String t){
        int next[]=new int[t.length()];
        next[0]=0;
        for (int i = 1,j=0; i < t.length(); i++) {
            while (j>0&&t.charAt(j)!=t.charAt(i))
                j=next[j-1];
            if(t.charAt(i)==t.charAt(j))
                j++;
            next[i]=j;
        }
        return next;
    }
}

求解 next数组

  • 前缀是除了最后一个字符的集合
  • 后缀是除了第一个字符的集合
    a b c a b d的前缀和后缀计算方法
    a 一个字符的前后缀都为0
    a b的前缀是 [ a ] 后缀是[ b ] 相同为 0
    a b c 的前缀是 [a ,a b] 后缀是 [b c,c] 相同为0
    a b c a 的前缀为 [a,ab,abc] 后缀是[b c a,c a,a] 0
    a b c a b 的前缀为 [a ,ab,abc,abca]后缀是 [bcab,cab,ab,b] 最大相同为ab next[4]=2
    a b c a b d 的前缀为 [a,ab,abc,abcb,abcabd]
    后缀为 [bcabd,cabd,abd,bd,d]最大共有长度还是ab 故 next[5] = 2;
  private static int[] getNext(String t){
        int next[]=new int[t.length()];
        next[0]=0;
        for (int i = 1,j=0; i < t.length(); i++) {
            while (j>0&&t.charAt(j)!=t.charAt(i))
                j=next[j-1];
            if(t.charAt(i)==t.charAt(j))
                j++;
            next[i]=j;
        }
        return next;
    }

小结

学习KMP算法花了我不少的时间,当时看了一直没有特别理解。后来经过自己画图,瞬间明白了;凡事还是要多动手。

参考

百度百科 KMP算法

扫描二维码关注公众号,回复: 10476632 查看本文章

过自己画图,瞬间明白了;凡事还是要多动手。

参考

百度百科 KMP算法

阮一峰的博客 字符串匹配的KMP算法

发布了91 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/WeDon_t/article/details/102611537