KMP
1.应用
给定两个字符串s1,s2,判断s2是否是s1的字串,若存在则返回在s2在s1中的起始下标,不存在则返回-1
2.概念引入
最长前缀和最长后缀的匹配长度
字符串:abbabb(不考虑自身长度的前缀后缀)
1 | 2 | 3 | 4 | 5 | |
---|---|---|---|---|---|
前缀 | a | ab | abb | abba | abbab |
后缀 | b | bb | abb | abba | bbabb |
从上表中可知字符串abbabb的最长前缀和最长后缀的匹配长度为3
3.流程
经典流程
s1 a b b b a b b b l ...
index i... x
s2 a b b b a b b b q ...
0... y
当我们匹配到x位置和y位置时我们发现不相等于是 s1回到 i+1位置 ,s2回到 0位置接着进行比较
时间复杂度O(n*m)
KMP流程
1.我们对s2计算每个位置最长前缀和最长后缀,结果记录next数组
s1 a b b b a b b b l ...
index i... x
s2 a b b b a b b b q ...
0 1 2 3 4 5 6 7 8
y
next -1 0 0 0 0 1 2 3 4
当我们匹配到x位置和y位置时我们发现不相等于是s1原地不动,s2回到next[y]位置,s1原地不动,接着进行比较,相当于我们把s2推进
s1 a b b b a b b b l ...
s2 a b b b a b b b q ...
next -1 0 0 0 0 1 2 3 4
index y
此时我们发现l != a,于是s1原地不动,s2回到next[y]位置,s1原地不动,接着进行比较,相当于我们把s2推进
s1 a b b b a b b b l ...
s2 a b b b a b b b q ...
index y
此时我们发现l != a,此时next[y]=-1,代表s2已经推不动了,您老s1移动到x+1位置吧(?位置)
s1 a b b b a b b b l ? ...
s2 a b b b a b b b q ...
4.代码实现
public class KMP {
public static int getIndexOf(String s1,String s2){
if(s2.length()>s1.length() || s1==null || s2==null || s1.length()<1){
return -1;
}
int[] nextArr = getNextArr(s2);
char[] chs1 = s1.toCharArray();
char[] chs2 = s2.toCharArray();
int i1=0;
int i2=0;
while(i1<s1.length() && i2<s2.length()){
if(chs1[i1]==chs2[i2]){
i1++;
i2++;
}else if(nextArr[i2]!=-1){
i2=nextArr[i2];
}else{
i1++;
}
}
return i2==s2.length()?i1-i2:-1;
}
public static int[] getNextArr(String s){
if(s.length()==1)return new int[]{-1};
int[] next=new int[s.length()];
next[0]=-1;
next[1]=0;
int cn=0;//当前要和i-1比较的位置
int i=2;
char[] chs = s.toCharArray();
while(i<chs.length){
if(chs[cn]==chs[i-1]){
next[i++]=++cn; //i位置的值已经确定下来了
}else if(cn>0){
cn=next[cn];
}else{
next[i++]=0;//没有与当前位置相同的字符
}
}
return next;
}
public static void main(String[] args) {
System.out.println(getIndexOf("lanqianyaochengweijiagoushi", "jiagoushi"));
}
}
如有错误,欢迎大家在评论区交流哦😊