next数组相关概念
借助算法导论的一段话解释前缀和后缀的含义。
举个例子,假设有字符串“abcd”,那么
1、前缀:a,ab,abc,abcd,
2、后缀:d,cd,bcd,abcd,
当然空串即是前缀也是后缀。
但是在求解next数组中的前缀和后缀时,前缀是不包含末尾的那一个字符,后缀不包含首位字符,这是因为如果包含的情况下,那么前缀和后缀的最长公共子串永远就是字符串本身。
这里声明一下:模式串从下标0开始,因此next[0] = -1,如果模式串从下标1开始,那么next数组所有元素加1。
next数组:当主串与模式串的某一个不匹配时,模式串回退的位置,比如当前主串的j位与模式串的i位不匹配,那么下一次就从模式串的next[i]位与主串的j位开始匹配。
next[i]:表示模式串的前i个字符(即0~i-1)的前缀和后缀的最长公共子串数。
这里是采用字符串下标从1开始分析。
求next数组的代码:next数组的第0个元素以-1开始。
public int[] getNext(String str) {
int[] next = new int[str.length()];
//初始化next
next[0] = -1;
int i = 0;
int j = -1;
while(i < str.length()-1) {
if(j == -1 || str.charAt(i) == str.charAt(j)) {
i++;
j++;
next[i] = j;
} else {
j = next[j];
}
}
return next;
}
KMP匹配算法
//str1是文本串,str2是模板串,next是str2的next数组
public int KMP(String str1,String str2,int[] next) {
//标记str1的匹配位置
int i = 0;
//标记str2的匹配位置
int j = 0;
while(i < str1.length()) {
//如果当前要匹配的位置加上模板串的剩余部分超过了文本串的长度,那么说明后面不可能在有匹配串
if(i + str2.length() - j - 1>= str1.length()) {
return -1;
}
//如果文本串的第i位与模板串的第j位匹配,那么匹配下一位字符
if(str1.charAt(i) == str2.charAt(j)) {
i++;
j++;
//如果模板串匹配完成,说明完成了一次匹配,返回模板串在文本串的起始位置
if(j == str2.length()) {
return i - j;
}
} else {
//如果当前文本串的第i位与模板串的第j位不匹配,那么让模板串的next[j]位与文本串的第i位进行匹配
j = next[j];
//如果j= -1,说明文本串的第i+1位开始与模板串的第0位开始匹配
if(j == -1) {
i++;
j++;
}
}
}
return -1;
}
变形:如何找出文本串中所有的模板串?
假设文本串“abababab”,模板串“abab”,那么第一次匹配完成是在文本串的第3位和模板串的第3位(下标从0开始),这种情况我们需要找到文本串的“abab”的最长公共前后缀,即next[4],在上面的求next数组的算法中,发现并没有求next[str.length],因此这里只需要求出让next数组长度为模板串的长度加1
牛客中的一道KMP算法:
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 计算模板串S在文本串T中出现了多少次
* @param S string字符串 模板串
* @param T string字符串 文本串
* @return int整型
*/
public int kmp (String S, String T) {
int[] next = getNext(S);
//System.out.println(next.length);
int i = 0;
int j = 0;
int counter = 0;
while(i < T.length()) {
if(i + S.length() - j - 1>= T.length()) {
return counter;
}
if(T.charAt(i) == S.charAt(j)) {
i++;
j++;
if(j == S.length()) {
counter++;
// System.out.println("开始位置:"+(i-j));
j = next[j];
}
} else {
j = next[j];
if(j == -1) {
i++;
j++;
}
}
}
return counter;
}
public int[] getNext(String str) {
int[] next = new int[str.length() + 1];
//初始化next
next[0] = -1;
int i = 0;
int j = -1;
while(i < str.length()) {
if(j == -1 || str.charAt(i) == str.charAt(j)) {
i++;
j++;
next[i] = j;
} else {
j = next[j];
}
}
return next;
}
}