之前了解了下這個算法,實現(xiàn)了下,有問題歡迎指教
package org.huangry.colorful.common.utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
/**
* ClassName: KMPUtil
*
* @author huangruiying
* @version 1.0
*/
public class KMPUtil {
private Logger logger = LoggerFactory.getLogger(KMPUtil.class);
//計算后的序列
private static int[] substring_sequence_arr;
private String parentStr = "";
private String childStr = "";
public KMPUtil(String parentStr, String childStr){
this.parentStr = parentStr;
this.childStr = childStr;
//處理子串:生成子串序列到substring_sequence_arr
generate_substring_sequence(childStr);
logger.info(">>> KMP init success");
logger.info(">>> substr_seq_array {}",Arrays.toString(substring_sequence_arr));
}
/**
* 查詢母串內(nèi)是否包含子串
* 然后返回索引
* 不包含時 索引為 -1
* 包含時 索引為第一次出現(xiàn)的位置
*/
public int start(){
//通過KMP查詢
int index = search(parentStr,childStr);
logger.info("子串在目標(biāo)字符串的索引: {}", index);
return index;
}
/**
* 獲取母串內(nèi)包含子串的個數(shù)
* @return
*/
public int getCount(){
return count(parentStr,childStr);
}
/**
* KMP查詢
* @author huangruiying
* @param target 目標(biāo)字符串
* @param substring 待查詢子串
* @return 查找到的子串在目標(biāo)字符串中的起始索引
*/
private int search(final String target, final String substring) {
final char[] target_arr = target.toCharArray();
final char[] substring_arr = substring.toCharArray();
//初始化游標(biāo) i:target游標(biāo) j:子串游標(biāo)
int i=0,j=0;
//邊界
while (i < target_arr.length){
char target_val = target_arr[i];
char substring_val = substring_arr[j];
if(target_val == substring_val){
//跳出條件 (-1 : 長度跟索引比較,所以要-1)
if(j == (substring.length()-1)){
/**
* 返回起始索引
* target&index:sssa&0123
* substr&index:sa&01
* 返回 (3-1)= 2 , 2為substring sa第一字在target中出現(xiàn)的索引位置。
*/
return i-j;
}
i++;
j++;
}else{
int before_j = j - 1;
//子串邊界
if(before_j < 0){
j = 0;
}else{
j = substring_sequence_arr[before_j];
}
//未尋找到時的跳出條件
if(i == (target.length()-1)){
return -1;
}
}
}
return -1;
}
private int count(final String target, final String substring) {
int count = 0;// 計數(shù) target 內(nèi)包含多少個 substring
final char[] target_arr = target.toCharArray();
final char[] substring_arr = substring.toCharArray();
//初始化游標(biāo) i:target游標(biāo) j:子串游標(biāo)
int i=0,j=0;
//邊界
while (i < target_arr.length){
char target_val = target_arr[i];
char substring_val = substring_arr[j];
if(target_val == substring_val){
if(j == (substring.length()-1)){
count ++ ;
i ++ ;
j = 0;
continue;
}
i++;
j++;
}else{
int before_j = j - 1;
//子串邊界
if(before_j < 0){
j = 0;
}else{
j = substring_sequence_arr[before_j];
}
//未尋找到時的跳出條件
if(i == (target.length()-1)){
return count;
}
i ++ ;
}
}
return count;
}
/**
* 生成子串序列
* 子串:abcdabca
* 序列:00001231
* 規(guī)則:使用i,j兩游標(biāo),
* 初始化變量i=0,j=1
* 初始化序列數(shù)組 0 0 0 0 0 0 0 0
* 對比i與j所在索引處的數(shù)組值是否相等
* 若相等,那么j處序列數(shù)組值為i+1(索引+1) ,然后i,j分別向后移動一位
* 若不相等,查看序列數(shù)組內(nèi)當(dāng)前i-1位置的值(索引),并將該值賦值給當(dāng)前變量i 結(jié)束本次循環(huán)(注意i的邊界情況)
* @author huangruiying
* @param substring 子串
*/
private void generate_substring_sequence(String substring){
substring_sequence_arr = new int[substring.length()];
char[] substring_arr = substring.toCharArray();
//游標(biāo)
int i=0,j=1;
while (j<substring_arr.length){
char iChar = substring_arr[i];
char jChar = substring_arr[j];
if(iChar == jChar){
substring_sequence_arr[j] = i+1;
j++;
i++;
}else{
int before_i = i-1;
//邊界
if(before_i < 0){
substring_sequence_arr[j] = 0;
j++;
}else{
//通過序列內(nèi)當(dāng)前i的前一個值,改變i游標(biāo)
i = KMPUtil.substring_sequence_arr[before_i];
}
}
}
}
}