1. 假设模式串(子串)p: 为 "bbabba" ,扫描模式串的指针为j, 当扫描到模式串不匹配的时候模式串回退的位置为k
需要弄清楚的是next数组的含义,比如next[j] = k表示的是当p[j] == p[k]的判断不成立的时候(失配)k应该回退到模式串的下标为k的next[k]位置上
比如模式串为bbabba,下面是求解过程
b b a b b a
(p[k]为该行中最后一个字符,写成下面的形式是为了更好的观察,其实还是同一个字符串)
p[0] = b b netx[0] = -1 j = 0 k = -1
p[1] = b b b next[1] = 0 j = 1 k = 0
p[2] = a b b a next[2] = 1 j = 2 k = 1
p[3] = b b b a b next[3] = 0 j = 3 k = 0
p[4] = a b b a b a next[4] =1 j = 4 k = 1
p[5] = a b b a b b a next[5] = 2 j = 5 k = 2
要求解next[2],首先要看上一次的p[j]和p[k] 其中上一次的j和k为:j = 1 k = 0
判断p[1]是否等于p[0] 假如相等则next[2] = ++k = 1, 假如不相等k = next[k],在这里例子中p[1] = p[0] ,所以next[2] = ++k = 1
求解next[3] , 其中next[2] = 1 : j = 2 k = 1 那么判断p[2]是否等于p[1] ,这里p[2]!=p[1]所以 j = 2 k = next[1] = 0 判断p[2] 是否等于p[0] 发现不等那么j = 2 k = next[0] = -1,此时 k = -1,当k = -1 的时候next[3] = ++k = 0
求解next[4], 其中next[3] = 0 : j = 3 k = 0 那么判断p[3]是否等于p[0] 发现相等那么next[4] = ++k = 1
求解next[5], 其中next[4] = 1 : j = 4 k = 1 那么判断p[4]是否等于p[1] 发现相等那么next[5] = ++k = 2
那么模式串 "bbabba"的next数组为 : -1 0 1 0 1 2
上面的模式是基于代码的思路来的
求解next数组实际上也是求解最长前缀与最长后缀的最长匹配,比如像上面的模式串 "bbabba"(求解next[k]不包含模式串下标为k这个位置的字符所求解的前缀和后缀)
第一次next[0]]: b next[0] = -1
第二次next[1]: bb next[1] = 0
第三次next[2]: bba 求解next[2]的时候不包括bba的最后一个字符,所以bba的前缀为: b
后缀为b 所以最长前缀与最长后缀的最长匹配为b,有一个所以next[2] = 1
第四次next[3]: bbab bbab的前缀为: b和bb
后缀为a和ba,前缀和后缀都不匹配,所以next[3] = 0
第五次next[4]: bbabb bbabb的前缀为: b, bb,bba
后缀为b,ab,bab,所以最长前缀与最长后缀的最长匹配为b,有一个所以next[4] = 1
第六次next[5]: bbabba bbabba的前缀为: b, bb,bba,bbab
后缀为b,bb,abb,babb,所以最长前缀与最长后缀的最长匹配为bb,所以next[5] = 2
2. 下面是具体的代码实现:
public class Main{
public static void main(String[] args) {
//该方法也可以用来求解字符串中存在的匹配的字符串在源串中匹配的次数因为有计数变量count
String s = "babababcbabababb";//长度为16
//String p = "bababb";
String p = "babab";
System.out.println(indexOf1(s,p));
}
private static int indexOf1(String s,String p){//s:"babababcbabababb"
if(s.length()==0||p.length()==0)return -1;//p:"bababb"
if(p.length()>s.length())return -1;
int next[] = next(p);//next:{-1,0,0,1,2,3} p:bababb
int i = 0;//s位置
int j = 0;//p位置
int sLen = s.length();
int pLen = p.length();
int count = 0;
while(i<sLen){
if(j==-1||s.charAt(i)==p.charAt(j)){
i++;
j++;
}else{
j = next[j];
}
if(j==pLen){
count++;
//j回溯到上一个位置
j=next[j-1];
//i往前走一格
i--;
//return i - j;
}
}
return count;
}
private static int[] next(String ps) {
int pLength = ps.length();//pLength = 6
int next[] = new int[pLength];//next:{-1,0,0,0,0,0} pLength = 6
char p[] = ps.toCharArray();
next[0] = -1;
if(ps.length()==1){
return next; //ps:bababb
}
next[1] = 0;
int j = 1;
int k = next[j];
while(j<pLength-1){
if(k<0||p[j]==p[k]){
next[++j]=++k;
}else{
k = next[k];
}
}
return next;
}
}
其中还有其他例子:
String p = "ababaa"; //next:-1 0 0 1 2 3
String p = "bababb"; //next:-1 0 0 1 2 3
String p = "bbabba"; //next:-1 0 1 0 1 2
String p = "babbab"; //next:-1 0 0 1 1 2
想的时候可以求解next数组的时候是求解最长前缀与最长后缀的重叠部分