01.前置知识:字符串的前缀与后缀
如thank,
其前缀有:t,th,tha,than,thank
后缀有:thank,hank,ank,nk,k
02.KMP算法思想及执行流程
思想:每一趟比较中出现字符不等时,不需要回溯索引指针i,而是利用已经得到的部分匹配的结果将子串向右滑动尽可能远的距离,继续进行比较。
注意:kmp算法中,找的最长公共前后缀:长度要小于比较指针左端子串长度
03.求模式串的next数组
找最长公共前后缀(长度要小于比较指针左端子串长度)
3.1案例分析
案例1:abbabaababaa
字符 | a | b | b | a | b | a | a | b | a | b | a | a |
---|---|---|---|---|---|---|---|---|---|---|---|---|
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
next[i] | 0 | 0 | 0 | 1 | 2 | 1 | 1 | 2 | 1 | 2 | 1 | 1 |
最长公共前后缀 | 无 | 无 | 无 | a | ab | a | a | ab | a | ab | a | a |
next数组为:[0, 0, 0, 1, 2, 1, 1, 2, 1, 2, 1, 1]
案例2:ababaaaba
字符 | a | b | a | b | a | a | a | b | a |
---|---|---|---|---|---|---|---|---|---|
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
next[i] | 0 | 0 | 1 | 2 | 3 | 1 | 1 | 2 | 3 |
最长公共前后缀 | 无 | 无 | a | ab | aba | a | a | ab | aba |
next数组为:[0, 0, 1, 2, 3, 1, 1, 2, 3]
next[j+1]=value的含义:当j个位置发生不匹配时,(模式串中)比较指针回溯到第value个位置
3.2求模式串的next数组的代码
Java语言
private int[] kmpNext(String str) {
int[] next=new int[str.length()];
for (int i = 1,j=0; i < next.length; i++) {
while (j>0 && str.charAt(i)!=str.charAt(j)) {
j=next[j-1];
}
if(str.charAt(i)==str.charAt(j)) j++;
next[i]=j;
}
return next;
}
Scala语言
def kmpNext(str:String):Array[Int]={
var next=new Array[Int](str.length)
var j=0
for (i<-1 until next.length){
println(str(i)+" "+str(j)+" "+i+" "+j)
while (j>0 && str(i)!=str(j)){
j=next(j-1)
}
if(str(i)==str(j)){
j+=1
}
next(i)=j
}
return next
}
3.3模式串的next数组代码debug案例分析
假设模式串为“ababaaaba”,则模式串对应的字符数组arr为[a, b, a, b, a, a, a, b, a]
- ab: i=1,j=0 next[1]=0
- aba: i=2,j=0 arr[2]==arr[0],j++ j=1,next[2]=1
- abab: i=3,j=1 arr[3]== arr[1],j++ j=2,next[3]=2 (说明:由于前面已经证明arr[2]=arr[0]==>字符串arr[2]+arr[3]=字符串arr[0]+arr[1]最长公共前缀为2)
- ababa: i=4,j=2 arr[4]==arr[2],j++ j=3,next[4]=3
- ababaa: i=5,j=3 arr[5]!=arr[3],j=next[j-1]=next[2]=1
下一步=>
arr[5]!=arr[1],j=next[j-1]=next[0]=0下一步=>
arr[5]==arr[0],j++ j=1,next[5]=1 - ababaaa: i=6,j=1 arr[6]!=arr[1],j=next[j-1]=next[0]=0
下一步=>
arr[6]==arr[0],j++ j=1,next[6]=1 - ababaaab: i=7,j=1 arr[7]=arr[1],j++ j=2,next[7]=2
- ababaaaba: i=8,j=2 arr[8]=arr[2],j++ j=3,next[8]=3
最终next数组为[0, 0, 1, 2, 3, 1, 1, 2, 3]
04.kmp算法实现的全部代码
Java语言
public int indexOf(String haystack, String needle) {
if(needle.length()==0) return 0;
int[] next=kmpNext(needle);//获取next数组
for (int i = 0,j=0; i < haystack.length(); i++) {
while (j>0 && haystack.charAt(i)!=needle.charAt(j)) {
j=next[j-1];
}
if(haystack.charAt(i)==needle.charAt(j)) j++;
if(j==needle.length()) return i-j+1;
}
return -1;
}
private int[] kmpNext(String str) {
int[] next=new int[str.length()];
for (int i = 1,j=0; i < next.length; i++) {
while (j>0 && str.charAt(i)!=str.charAt(j)) {
j=next[j-1];
}
if(str.charAt(i)==str.charAt(j)) j++;
next[i]=j;
}
return next;
}
Scala语言
def indexOf(haystack: String, needle: String): Int = {
if (needle.length == 0) return 0
var next=kmpNext(needle)//获取next数组
var j=0
for(i<-0 until haystack.length){
while (j>0 && haystack(i)!=needle(j)) {
j=next(j-1)
}
if(haystack(i)==needle(j)) j+=1
if(j==needle.length) return i-j+1
}
return -1
}
def kmpNext(str:String):Array[Int]={
var next=new Array[Int](str.length)
var j=0
for (i<-0 until next.length){
while (j>0 && str(i)!=str(j)){
j=next(j-1)
}
if(str(i)==str(j)) j+=1
next(i)=j
}
return next
}