摘要
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)。
简介
字符串的匹配是对字符串的基本操作,最直接的方式是进行全遍历搜索的回溯法,但是其复杂度较高,而随着人们对于匹配效率的追求与匹配优化,其匹配的复杂度逐渐降低。
在这个过程中最重要的当属于KMP了,他将原有的算法复杂度降低到了O(n+m) 其中n,m是指两个字符串的长度。
详解
最直接的回溯法
从字符串左边开始匹配,如果遇到相同的sc与j同事进行+1,直到全部遍历完p的时候进行返回下标,如果匹配过程有不一样的sc进行回溯到i。
public class Match_kmp {
public static void main(String[] args) {
System.out.println(indexOf("aaavbvdd","vbv"));
}
private static int indexOf(String s,String p){
int i = 0;
int sc = i;
int j = 0;
while (sc<s.length()){
if(s.charAt(sc)==p.charAt(j)){
j++;
sc++;
if(j==p.length()){
return i;
}
}else
{
i++;
sc=i;
j=0;
}
}
return -1;
}
}
其结果是返回第一个匹配的下标i
- 这种求解的方法复杂度达到O(n*m),下面介绍KMP算法.
KMP匹配
图解
①从左到右进行匹配,此时 i =0,j=0;如果相同 j++,i++,如果不相同 j进行回溯
②匹配到最后一个不相同的时候进行回溯,如果暴力进行匹配的时候 i ++,令j=0;
但是我们可以借助之前已经比较的字符串T,进行回溯。这样比较的时候就会减少复杂度。j回溯的大小取决于匹配字符串T的next[数组]。Next数组的求解放到下面说.next={0,0,0,0,2,2}
右
移
的
大
小
=
以
经
匹
配
的
字
符
串
−
n
e
x
t
[
j
]
右移的大小=以经匹配的字符串-next[j]
右移的大小=以经匹配的字符串−next[j]
所以next[5]=2,右移的大小=5-2,此时j也进行回溯,
j
=
n
e
x
t
[
j
−
1
]
j = next[j-1]
j=next[j−1]
③以此类推,此时j=2;进行比较 ,不相同,进行回溯 j = 2-next[j-1]=2…
KMP
public class MatchKMP {
public static void main(String[] args) {
int ne[] =getNext("abcdabd");
int res = kmp("ssdfgasdbababa","bababa",ne);
System.out.println(res);
}
private static int kmp(String s,String t,int[] next){
for (int i = 0,j=0;i<s.length();i++){
while(j>0&&s.charAt(i)!=t.charAt(j)){
j=next[j-1];
}if(s.charAt(i)==t.charAt(j)){
j++;
}
if(j==t.length()){
return i-j+1;
}
}
return 0;
}
private static int[] getNext(String t){
int next[]=new int[t.length()];
next[0]=0;
for (int i = 1,j=0; i < t.length(); i++) {
while (j>0&&t.charAt(j)!=t.charAt(i))
j=next[j-1];
if(t.charAt(i)==t.charAt(j))
j++;
next[i]=j;
}
return next;
}
}
求解 next数组
- 前缀是除了最后一个字符的集合
- 后缀是除了第一个字符的集合
a b c a b d的前缀和后缀计算方法
a 一个字符的前后缀都为0
a b的前缀是 [ a ] 后缀是[ b ] 相同为 0
a b c 的前缀是 [a ,a b] 后缀是 [b c,c] 相同为0
a b c a 的前缀为 [a,ab,abc] 后缀是[b c a,c a,a] 0
a b c a b 的前缀为 [a ,ab,abc,abca]后缀是 [bcab,cab,ab,b] 最大相同为ab next[4]=2
a b c a b d 的前缀为 [a,ab,abc,abcb,abcabd]
后缀为 [bcabd,cabd,abd,bd,d]最大共有长度还是ab 故 next[5] = 2;
private static int[] getNext(String t){
int next[]=new int[t.length()];
next[0]=0;
for (int i = 1,j=0; i < t.length(); i++) {
while (j>0&&t.charAt(j)!=t.charAt(i))
j=next[j-1];
if(t.charAt(i)==t.charAt(j))
j++;
next[i]=j;
}
return next;
}
小结
学习KMP算法花了我不少的时间,当时看了一直没有特别理解。后来经过自己画图,瞬间明白了;凡事还是要多动手。
参考
百度百科 KMP算法
过自己画图,瞬间明白了;凡事还是要多动手。
参考
百度百科 KMP算法
阮一峰的博客 字符串匹配的KMP算法