字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)
假设字符串str长度为N,字符串match长度为M,M <= N
想确定str中是否有某个子串是等于match的,有返回匹配索引位置
时间复杂度 O(N)
str: aabaabaac
match: aaba
返回3,即str的索引3位置
KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的
求出match串的next数组,进行加速匹配
next数组是match串的前缀与后缀相同记录信息,即记录则每个位置的前面字符串,前缀与后缀相同的最大字符串数量【前缀不包含本身】
举例:match串为 aabaabsaabt,它的next数组应该是 [-1,0,1,0,1,2,3,0,1,2,3]
图示如下:
现在有了match串的next数组
就进行匹配过程
为了简单,match串为 aabaab ,next数组为 [-1,0,1,0,2]
str串为 bcaabaataabaab
x表示str串的匹配来到位置
y表示为match串匹配来到的位置
比如在3状态经过3过程调整到4状态时,即y在5,x在7位置时,match[5] = b 不等于 str[7] = t,接着y跳转到next[5] = 2位置,y来到 2位置,match[2] = b
因为
next 求得是前缀与后缀相等的最大缀字符串数;
而既然匹配到当前位置,那么说明match串当前位置的前面位置是在str中能匹配上的,而match串中前缀与后缀相同的记录,那么match前缀一定与str当前位置前面相同,因为str前面位置是后缀,
这样直接跳过前缀的匹配,进而加速整个过程
很明显,按照先前的思路设计代码,next的求解太耗费时间
那么next数组是否存在空间位置依赖呢,进而用动态规划得到next数组
必然是有的
过程描述:
求i位置的next值时,查看i-1位置的next[i-1],记为c,【c即为i位置的前缀匹配个数,也是str中前缀后一个位置的数的下标】
匹配str[c]和str[i-1],如果相等,那么next[i] = c+1;如果不相等,继续会退c,即c = next[c]
直到无法回退,next[i] = c+1
图示如下:
public static int getIndexOf(String str,String match){
//合法判断
if(str == null || str.length() == 0 || match == null || match.length() > str.length())
return -1;
//O(N)
char[] str1 = str.toCharArray();
char[] str2 = match.toCharArray();
//O(N)
int [] next = getNextArray(str2);
int x = 0;
int y = 0;
//O(N)
while (x < str1.length){
if(str1[x] == str2[y]){
x++;
y++;
}else if (next[y] == -1) {
x++;
} else {
y = next[y];
}
}
return y == str2.length ? x - y : -1;
}
public static int[] getNextArray(char[] str) {
if(str == null || str.length == 0)return null;
if(str.length == 1)return new int[]{-1};
int [] next = new int[str.length];
//初始值
next[0] = -1;
next[1] = 0;
int i = 2;
int c = 0;
while (i < str.length){
if(str[i-1] == str[c]){
next[i++] = ++c;
}else if(c > 0){
c = next[c];
}else{
next[i++] = 0;
}
}
return next;
}