七夕来临,不知巧合还是咋地,突然在LeetCode刷到不少字符串匹配的题目,本来抱着大力出奇迹心理的我一直以来都是暴力破万法,不料也卡在了时间复杂度上,于是拾起了久久不用的kmp算法。
字符串匹配
先来看一个场景:
现在有两个字符串,S1和S2,现在想知道S1中是否有S2。如何判断?
当然,最先肯定会想到的是indexOf()函数,亦或是正则表达式。然而,当需要我们自己编写算法时,也不能傻了眼,接下来认识一下几种编写匹配算法的方式:
1、暴力搜索
简单易懂的算法
算法描述:在原字符串搜索模式串,当发现无法匹配的情况,就一夜回到解放前,继续从原来的起始位置的下一个进行对模式串搜索。直到搜索成功或搜索完原字符串,下面说明一个例子:
程序如下:
public static int bf(String s1 , String s2){
int result = -1;
int l1 = s1.length();
int l2 = s2.length();
if(l1 == 0 || l2 == 0 || l1 < l2){
return -1;
}
A:for(int i = 0 ; i <= l1 - l2 ; i++){
for(int j = 0 ; j < l2 ; j++){
if(s1.charAt(i + j) != s2.charAt(j)){
continue A;
}
}
result = i;
}
return result;
}
其返回值为主串中第一次匹配模式串的索引
可见,这种做法虽然简单易懂,容易实现,但是比较笨拙,为此,我们可以引进KMP算法
KMP算法
观察到上述移动过程,可以发现:有很大的改进余地,每一次判断都浪费了之前已经判断出来的某些信息,为此,我们对其进行改进:
可以通过一个next数组,保存前面已判断出的信息,能做到使主串中的i不用往回进行回溯
代码如下:
public static int kmp(String str,String pat){
int[] next = next(pat);
for(int i = 0 ,j = 0 ; i< str.length();i++){
while(j > 0 && str.charAt(i)!= pat.charAt(j)){
j = next[j - 1];
}
if(str.charAt(i) == pat.charAt(j)){
j++;
}
if(j == pat.length()){
return i - j + 1;
}
}
return -1;
}
public static int[] next(String pat){
int[] next = new int[pat.length()];
next[0] = 0 ;
for(int i = 1 , j = 0 ; i < pat.length(); i++){
while (j > 0 && pat.charAt(i) != pat.charAt(j)){
j = next[j - 1];
}
if(pat.charAt(i) == pat.charAt(j)){
j++;
}
next[i] = j;
}
return next;
}