KMP算法分析

1、问题出现

给出一个字符串为source,和另一个字符串target。

如果这个target是source的字串,返回这个子串的开始的索引。如果不是子串则返回-1。

2、解决问题的思路

任何一个算法都说从暴力法优化得到的,KMP算法也不例外。这次我们列出暴力法如下:

 1 public class KMP {
 2     public static int match(String source,String target) {
 3         int i=0;//原字符串的下标
 4         int j=0;//目标子串的下标
 5         int k=-1;//记录第几个字符
 6         while(i<source.length()) {
 7             while(j<target.length()&&source.charAt(i)==target.charAt(j)) {
 8                 i++;
 9                 j++;
10             }//子串和目标串进行匹配
11             k++;//首字符位置每次循环后+1。
12             if(j==target.length()) {
13                 return k;
14             }else {
15                 j=0;
16                 i=k+1;
17             }
18             
19         }
20         return -1;
21     }
22     public static void main(String[] args) {
23         System.out.println(match("abababca","bab"));
24     }
25 }

 

 

这个算法有俩层循环嵌套显得十分臃肿。这时候就需要对算法进行优化。

3、KMP算法的引入

上面的算法每次匹配失败之后,我们会都移动i和j。

而KMP则只需要我们每次只移动需要匹配的字串的j 不移动原字符串的i。这样就少了一层循环。

那j需要移动到哪呢?KMP为原字符串准备了个next数组。

先不用管如何实现的。我们先试试这个方法。

public class KMP2 {
    static int[] next = {-1,0,0,1,2,3,4,0};
    public static int match(String source,String target){
        int i=0;
        int j=0;
        
        while(i<source.length()) {
            if(j == -1 ||source.charAt(i)==target.charAt(j)) {
                i++;
                j++;
            }
            else {
                j=next[j];//这里我们只给j赋值
            }
            if(j==target.length()) {
                return i-j;//返回匹配开头的字符
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        System.out.println(match("abababca","bab"));
    }
}

 

结果是一样的。这就少了一层循环。

4、NEXT数组如何求的呢?

总共分4步骤

1、找字符串的前缀式:例如 aba这个 前缀式就是a、ab

2、找字符串的后缀式:例如 aba这个字符串的后缀式为ba、a

3、找前后俩个字符串相同的最长字串、的长度,这里的aba中a相同,因此就是1

但是如果未找出就得0;

如上所示。

4、把我们之前求的数,向右移。一位就是next数组了。

此图像的alt属性为空;文件名为@W6IAKAKJ855TKGFEKOZ7.png

如果之前没做过的,一定要手动算一下。算完了就记住了。

5、算法求出NEXT数组。

算法实现是个难理解的地方。

记住俩概念:索引值为0和1的俩个数设为-1和0,不为什么。记住

public class KMP2 {
    //static int[] next = {-1,0,0,1,2,3,4,0};
    public static int[] getNext(String source)
    {
        int[] next = new int[source.length()];
        // 初始条件
        int j = 0;
        int k = -1;
        next[0] = -1;
        // 根据已知的前j位推测第j+1位
        while (j <source.length() - 1){
            if (k == -1 || source.charAt(j) ==source.charAt(k)){
                next[++j] = ++k;
            }
            else{
                k = next[k];
            }
        }
         return next;
    }
    
    
    public static int match(String source,String target){
        int i=0;
        int j=0;
        int[] next = getNext(source);
        while(i<source.length()) {
            if(j == -1||source.charAt(i)==target.charAt(j)) {
                i++;
                j++;
            }
            else {
                j=next[j];
            }
            if(j==target.length()) {
                return i-j;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        System.out.println(match("abababca","bab"));
    }
}

 

 

posted @ 2019-05-08 21:12  秃桔子  阅读(270)  评论(0编辑  收藏  举报

如果您有编程方面或者学术方面的需求请在微信公众号搜索

桔子科研


或者识别下方二维码,第一时间获取编程有趣的知识和最新科研学术成果。