子串匹配问题:返回一个子串在字符串中第一次出现的下标,若无则返回-1
KMP:
一个朴素的子串匹配如下:(以下使用TypeScript语言编写)
private subStringIndex(source:string,target:string):number{
if(target.length>source.length||!source||!target){
return -1;
}
let res=-1;
for(let i=0;i<=source.length-target.length;i++){
if(source[i]==target[0]){
let temp=true;
for(let j=0;j<target.length;j++){ //可以看到使用了两层的for循环,时间复杂度是0(n^2)
if(source[i+j]!=target[j]){
temp=false;
break;
}
}
if(temp){
res=i;
return res
}
}
}
return res;
}
在子串与母串不匹配时,母串的i会回退到子串第一个的位置,减少这个回退的数量就是KMP的目标
KMP:通过getNext获得当source[i+j]!=target[j]时i向前移动多少位置
next=当匹配到这个字符不一致时,子串上从开始到这个字符的串中前缀与后缀的最大公共部分+1,比如 母串是:abcdabae, 子串是aba ,子串会在index=2处不一致,前后缀最长公共部分是a,(前缀a、ab,后缀a、ba)next=2 ,母串的匹配再从跳到(a+2)c开始,而c!=a,在index=0处不一致子串的前后缀最长公共部分是空 next=1,母串只跳一步
比如:
模式串 a b a a b c a c
next值 0 1 1 2 2 3 1 2
如何通过代码获得next数组:
private getNext(s:string):number[]{
let next=[];
next[0]=-1; //哨兵
let i=0;
let j=-1;
while(i<s.length){
if(j==-1||s[i]==s[j]){
i++;
j++;
next[i]=j;//如果一直相同比如aaaaa/abababa会生成[-1,0,1,2,3,...]
}else{
j=next[j];//j在i之前,这里会将j回退直到j==-1或者匹配时
}
}
return next;
}
如果难理解也可以看以下另一种方法得到的next数组
public getNext(s:string):number[]){
let next=[];
next[0]=0;
for(let i=1,j=0;i<s.length;i++{
while(j>0&&s[j]!=s[i]){
j=next[j-1];
if(s[j]==s[i]){
j++;
}
next[i]=j;
}
}
}
private subStringIndexKMP(source:string,target:string):number{
if(target.length>source.length||!source||!target){
return -1;
}
let res=-1;
let i=0;
let next:number[]=this.getNext(source);
while(i<=source.length-target.length){
if(source[i]==target[0]){
let temp=true;
for(let j=0;j<target.length;j++){
if(source[i+j]!=target[j]){
temp=false;
i+=next[j+1]; //i向前移动j+1跳过哨兵
break;
}
}
if(temp){
res=i;
return res
}
}else{
i++;
}
}
return res;
}
let s="abababca"
let yi="abc";
let y="bab";
console.log(this.subStringIndexKMP(s,yi));//输出4
console.log(+this.subStringIndexKMP(s,y));//输出1
来源:CSDN
作者:阿凡达的忧伤
链接:https://blog.csdn.net/QQ734821120/article/details/99729636