一.简介
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败字符前面已匹配字符计算跳转表,子串从跳转表获取下一次比较索引。
定义问题:
主串:
s = S[0,1,2,…,n-1] 字符串长度为n
子串:
p = P[0,1,…,m-1], 字符串长度为m且m<=n
字符串 abcdbcd
真前缀:除最后一个字符外含有头部字符的顺序组合
abcdbc,abcdb,abcd,abc,ab,a 都是真前缀
真后缀:除头部字符外含有最后一个字符的顺序组合
bcdbcd,cdbcd,dbcd,bcd,cd,d 都是真后缀
对于长度为k的字符串,其真前缀(真后缀)长度为:[0,k-1]
暴力匹配
1.从主串的首字符开始,与模式串逐一进行匹配
2.匹配失败时主串回溯到开始匹配索引的下一个索引开始,模式串从0继续开始匹配
3.时间复杂度:O(n * m)
推导:
匹配过程中部分匹配时主串回溯匹配做了很多无用比较。有 k 个匹配字符时的主串与子串有如下关系:
1.S[i,i+1,i+2,…,i+k-1] = P[0,1,…,k-1] 且 S[i+k] 不等于 P[k],k <= m;
2.暴力匹配的下一次比较主串 i = i-k+1,模式串 j = 0,如果回溯会有匹配的可能,定有如下关系:
P[0,1,…,j-1,j] = S[i,i+1,i+j-1],即等价于模式串中最开始的一部分和模式串最后一部分有重叠,即 P[0,1,…,k-1] = P[m-k,m-k+1,…,m-1],否则都是无效比较;
3.基于推论2,此时回溯主串索引已经没有意义(重叠部分一定是相等的),主串索引继续在上次不匹配的位置,回溯模式串索引为 k 再比较即可
基于推论3可知如果提前计算模式串在匹配k个字符时的相等最大真前缀、真后缀就能提高匹配效率。
二.实现
package com.vincent;
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws Exception {
String src = "abcdabcabcde";
String dst = "abcabc";
System.out.println(bruteSearch(src,dst));
int[] table = next(dst);
System.out