问题描述

在文本中找到模式字符串首次出现的位置。

文本:String text

模式字符串:String pattern

概念

1、前缀:表示包含首位字符但不包含末位字符的子串

2、后缀:表示包含末位字符但不包含首位字符的子串

3、next数组

  • next[i]:表示模式字符串的子串 pattern[0,1,……, i] 中,前后缀相等的长度有多长。

代码实现

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main2 {

    public static void main(String[] args) {
        String text="ABCDABEABCDABD";
        String pttern="ABCDABD";
        int next[]=kmpNext(pttern);
        System.out.println(Arrays.toString(next));
        int result=kmpSearch(text,pttern,next);
        System.out.println(result);
    }

    public static int kmpSearch(String text, String pattern, int[] next) {
        for(int i = 0, j = 0; i < text.length(); i++) {
            while( j > 0 && text.charAt(i) != pattern.charAt(j)) {//必须有j>0,因为要确保j-1大于0!
                j = next[j-1];
            }
            if(text.charAt(i) == pattern.charAt(j)) {
                j++;
            }
            if(j == pattern.length()) {
                return i - j + 1;//完全匹配的时候 j = parttern.length 超出parttern下标范围,所以要加上1。
            }
        }
        return  -1;
    }

    public static  int[] kmpNext(String pattern) {
        int[] next = new int[pattern.length()];
        next[0] = 0;
        for(int i = 1, j = 0; i < pattern.length(); i++) {
            //这时kmp算法的核心点
            while(j > 0 && pattern.charAt(i) != pattern.charAt(j)) {
                j = next[j-1];
            }
            if(pattern.charAt(i) == pattern.charAt(j)) {
                j++;
            }
            next[i] = j;
        }
        return next;
    }

}

过程理解

KMP算法_字符串

如上图所示,text="ABCDABEABCDABD",pattern="ABCDABD"。当比较到 i = 6,j = 6的时候,发现不匹配。此时对应的代码是

while( j > 0 && text.charAt(i) != pattern.charAt(j)) {
    j = next[j-1]; 
}

当前 j = 6,则 执行语句 j = next[j-1],j 的值更新为2。将 text[6]与pattern[2] 比较,发现还是不相等。继续循环。

当前 j = 2,则 执行语句 j = next[j-1],j 的值更新为0。将 text[6]与pattern[2] 比较,发现还是不相等。因为循环条件 j>0 不成立,结束循环。当前 i = 6, j = 0。执行以下代码。

if(text.charAt(i) == pattern.charAt(j)) {
    j++;
}            
if(j == pattern.length()) {
    return i - j + 1;
}

将 text[6]与pattern[2] 比较,发现不相等,所以第一个 if 语句不成立。

当前 j = 0 ,第二个 if 语句也不成立。当前循环结束,i++。当前 i = 7,j = 0。

KMP算法_i++_02

参考链接

1、 帮你把KMP算法学个通透

2、 很详尽的KMP算法