kpm字符串匹配算法

首先是简单的朴素匹配算法

    /*
     * 返回子串t在主串s的位置,若不存在则返回0
     */
    public static int index(String s, String t) {
        
        int i = 0;//i记录主串当前位置的下标
        int j = 0;    //j记录子串当前位置的下标
        
        char[] c = t.toCharArray();
        char[] d = s.toCharArray();
        while(i < s.length() && j < t.length()) {
            if(c[i] == d[j]) {
                i++;
                j++;
                if(j == t.length()) {
                    break;
                }
            }else {
                i = i - j;//如果不匹配i退回到上次匹配首位的下一位
                j = 0;
            }
        }
        if(j == t.length()) {
            return i - j + 1;
        }
        return 0;
    }

举例说明:

s是 abcabcabd t是 abcabd,朴素的匹配算法每次发现不对都要重新回到上次匹配的首位,也就是要重新在s从找一次t的和第一个字符匹配的字符。

但是像这个例子t字符串中一开始就有ab后面也有ab,也就是说如果匹配到最后一位发现不匹配的时候,就可以直接进行到这里

所以这就是kmp改进的地方,先自行处理一下t字符串,找到t字符串中与前缀有重复的。建立一个next数组记录下这个重复,就比如这个t匹配到最后一个时发现s[5]和t[5]匹配失败,但是t[3]t[4]和t[1]t[2]是重复的,所以这里可以直接进行s[5]和t[3]的匹配。从代码的角度讲就是当i=5,j=5时,s[i]和t[j]匹配失败,那就修改j跳过之前与t字符串前缀重复的。

这是一个next数组,这里j=0时没有前缀所以在匹配的时候就应该重t[0]开始匹配,j=1时前缀是a但是t[1]是b所以没有重复,所以还是0,一直到j=3,a和前缀a重复所以所以在匹配的时候就应该重t[1]开始匹配,到j=4,ab和前缀ab重复所以所以在匹配的时候就应该重t[2]开始匹配,j=5的时候abd和前缀abc并没有重复所以应该重t[0]开始匹配。

再然后就是这个next数组怎么计算。。

声明一个k初始化为0用于记录前缀被重复的长度了,就比如t[3]和t[0]重复就记1,此时next[]相应的位置就是1(k),t[3]t[4]和t[0]t[1]重复就记2,此时此时next[]相应的位置就是2(k),t[3]t[4]t[5]t[0]t[1]t[3不]重复就置0。

整体代码:

public static int KmpIndex(String s, String t) {
        int i = 0;
        int j = 0;    //j记录子串当前位置的下标
        int[] next = new int[t.length()];
        char[] c = t.toCharArray();
        char[] d = s.toCharArray();
        int k = 0;
        
        while(j < t.length()) {
            if(k == 0 && j == 0) {
                //第一个字符的前后缀为空
                next[j++] = 0;
            }else {
                //使用k来记住已经和前者匹配到那个位置了
                if(k == 0 ) {
                    //k=0 就是上一个未匹配
                    if(c[k] != c[j]) {
                        //不相等就是不匹配
                        next[j++] = 0;
                    }else {
                        //相等就是匹配,next[j]等于k+1,然后增加k和j的值
                        next[j++] = ++k;
                    }
                }else {
                    //k!=0 就是上k个都已经匹配了
                    if(c[k] != c[j]) {
                        //不相等就是不匹配而且之后要重头匹配 k=0
                        next[j++] = 0;
                        k = 0;
                    }else {
                        //相等就是匹配,next[j]等于k+1,然后增加k和j的值
                        next[j++] = ++k;
                    }
                }
            }
        }
        j = 0;
        while(i < s.length() && j < t.length()) {
            if(d[i] == c[j]) {
                i++;
                j++;
                if(j == t.length()) {
                    break;
                }
            }else {
                j = next[j];
            }
        }
        if(j == t.length()) {
            return i - j;
        }
        return 0;
    }
原文地址:https://www.cnblogs.com/xxbbtt/p/7625726.html