问题
已知某个主串和子串,现要求求出字串p第一次出现在主串s中的位置。
算法讲解
这一算法看了很多材料终于搞懂,非常不容易。无论是教材还是其他博客,演示过程太过抽象,推荐以下视频教程:
- https://www.bilibili.com/video/av49930100?t=664
- https://www.bilibili.com/video/av91575042?t=1670&p=82 (P82 P83)
算法思想
算法的核心在于准确理解next[]数组的含义。next[j]的含义是:当字串p在第j位失配(字符不匹配)时指针j所要跳转到子串p的位置。
求next数组:比较j和k上的字符是否相同,
- 不同 则 k = next[k] 一直回溯直到Pj = Pk或者k<0
- 相同 则 next[j+1] = k+1
(求next数组是一个递推的过程,要想求得第j的k值(即:next[j] )则需要参考next[j-1]在其基础上比较j -1和 next[j-1]的字符是否相同求得,如果相同,则在其结果上加1,k值就一直回溯,直到k小于0.)
逻辑如下:
如果 Pj = Pk , next(j+1) = k+1 或者 k<0 时 ,next(j+1) = k+1; j++; k++;
否则:k继续回溯直到满足 Pj = Pk 或者 k < 0.
求解
- 暴力算法
private static int indexOf1(String s, String p){
int i = 0;
int j = 0;
int sc = i; //扫描指针每次从i开始
while(sc<s.length()){
if(s.charAt(sc)== p.charAt(j)){
sc++;
j++;
if(j==p.length()){
return i;
}
}else{ //在某一位不匹配
i++; //主串移动到下一位
sc = i; //回溯到i的下一位
j = 0; //字串回溯到0 从新开始
}
}
return -1;
}
- KMP算法
public class KMP {
public static void main(String[] args) {
String src = "babababcbabababcba";
int index = indexOf(src, "ababc");
System.out.println(index);
}
private static int indexOf(String s, String p) {
int sLen = s.length();
int pLen = p.length();
int[] next = next(p);
int i = 0;
int j = 0 ;
while(i<sLen){
//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++
//j=-1,因为next[0]=-1,说明p的第一位和i这个位置无法匹配,这时i,j都增加1,i移位,j从0开始
if(j==-1||s.charAt(i) == p.charAt(j)){
i++;
j++;
}else{
//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]
//next[j]即为j所对应的next值
j = next[j]; //第j位失配了,j指向其他位置
}
if(j==pLen){//匹配成功
return i - j;
}
}
return -1;
}
private static int[] next(String p){
int pLen = p.length();
int[] next = new int[pLen];
next[0] = -1;
if(p.length()==1){
return next;
}
next[1]=0;
int j = 1;
int k = next[j];//看看位置j的最长匹配前缀在哪里
while( j<pLen -1){
if( k<0 || p.charAt(j) == p.charAt(k)){
next[++j] = ++k; //现在要推出next[j+1],检查j和k位置上的关系即可
}else{
k = next[k];
}
}
return next;
}
}
算法比较
- 暴力算法 时间复杂度 O(m*n)
- KMP 时间复杂度 O(m+n)
算法扩展
题目:已知某个主串和子串,现要求求出子串p出现在主串s中的次数。
代码在上面代码基础上改造如下:
private static int indexOf(String s, String p) {
int sLen = s.length();
int pLen = p.length();
int[] next = next(p);
int i = 0;
int j = 0 ;
while(i<sLen){
if(j==-1||s.charAt(i) == p.charAt(j)){
i++;
j++;
}else{
j = next[j]; //第j位失配了,j指向其他位置
}
if(j==pLen){//匹配成功
// return i - j;
count++;
i--;
j = next[j - 1];
}
}
return -1;
}
运行展示
测试代码如下:
public static void main(String[] args) {
String src = "babababcbabababcba";
int index = indexOf(src, "bab");
System.out.println(count);
}