经典的字符串匹配算法,KMP:非常经典,但比较难理解,且效率不是很高; Sunday:效率比kmp高很多,而且理解起来比较容易;
前言:继上次博文已经有半个月了,虽然没有粉丝,但我拖了自己的更。在此解释一下,因为前段时间家中奶奶去世,我请假回家了几天,然后回到武汉后又生病了,所以就一直没有更新博客。
正文:
1、KMP算法
KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。
与暴力匹配相比:
暴力算法: 每次失配,S串的索引i定位的本次尝试匹配的第一个字符的后一个。P串的索引j定位到1;T(n)=O(n*m)
KMP算法: 每次失配,S串的索引i不动,P串的索引j定位到某个数。T(n)=O(n+m),时间效率明显提高
其实主要就是求这个next[j]数组
代码实现:
/**
* 求一个字符数组的next数组
* @param t 字符数组
* @return
*/
public static int[] getNextArray(char[] t){
int[] next = new int[t.length];
next[0] = -1;
next[1] = 0;
int k;
for(int j = 2; j < t.length; j++){
k = next[j-1];
while( k!= -1){
if(t[j-1] == t[k]){
next[j] = k + 1;
break;
}else {
k = next[k];
}
next[j] = 0;
}
}
return next;
}
/**
* kmp实现
* @param s 主字符串
* @param t 模板字符串
* @return
*/
public static int kmpMatch(String s,String t){
char[] s_arr = s.toCharArray();
char[] t_arr = t.toCharArray();
int[] next = getNextArray(t_arr);
int i = 0,j = 0;
while(i < s_arr.length && j < t_arr.length){
if(j == -1 || s_arr[i]==t_arr[j]){
i++;
j++;
}else
j = next[j];
}
if(j == t_arr.length)
return i-j;
else
return -1;
}
public static void main(String[] args) {
System.out.println(kmpMatch("abcabaabaabcacb", "abaabcac"));
}
虽然经典,奈何KMP难啃效率低。所以个人更推荐Sunday算法
2、Sunday算法
public static int contains(char[] str,char ch){
for(int i=str.length-1;i>=0;i--){
if(str[i]==ch){
return i;
}
}
return -1;
}
public static void sunday(String s, String t) {
char[] sarray = s.toCharArray();
char[] parray = t.toCharArray();
int slen = s.length();
int plen = t.length();
int i = 0, j = 0;
while (i <= slen - plen + j) { //这句话控制索引i,j的范围
if (sarray[i] != parray[j]) { //假如主串的sarry[i]与模式串的parray[j]不相等
if (i == slen - plen + j) {
break;
/**假如主串的sarry[i]与模式串的parray[j]不相等,并且i=slen-plen+j,说明这已经
是在和主串中最后可能相等的字符段比较了,并且不相等,说明后面就再也没有相等的了,所以
跳出循环,结束匹配*/
}
//假如是主串的中间字段与模式串匹配,且结果不匹配,则就从模式串的最后面开始,
//(注意是从后向前)向前遍历,找出模式串的后一位在对应的母串的字符是否在子串中存在
int pos = contains(parray, sarray[i + plen - j]);
if (pos == -1) { //表示不存在
i = i + plen + 1 - j;
j = 0;
} else {
i = i + plen - pos - j;
j = 0;
}
} else { //假如主串的sarry[i]与模式串的parray[j]相等,则继续下面的操作
if (j == plen - 1) { //判断模式串的索引j是不是已经到达模式串的最后位置,
//j==plen-1证明在主串中已经找到一个模式串的位置,且目前主串尾部的索引为i,
//主串首部的索引为i-j,打印模式串匹配的第一个位置
System.out.println("the start pos is " + (i - j) + " the end pos is " + i);
//然后主串右移一个位置,再和模式串的首字符比较,从而寻找下一个匹配的位置
i = i - j + 1;
j = 0;
} else {
//假如模式串的索引j!=plen-1,说明模式串还没有匹配完,则i++,j++继续匹配,
i++;
j++;
}
}
}
}
最后:说来惭愧,其实KMP我研究了好几天,到现在也不敢完全理解。而且更好的理解的话最好画一下图,我学习的时候也画了不少图,可惜没保存下来,所以就没贴出来。
如果有人像我一样觉得KMP太难理解的话,不要灰心,推荐直接把相对简单的Sunday算法研究好,最好能手撸一遍实现过程。