字符串匹配算就是给定两个串,查找一个串是否在另一个串里面。为解决这个问题由此衍生了两个算法,B BF 算法和 KMP 算法。下面就来了解一下这两个算法吧。
1.用java实现 BF 算法
BF算法又称为暴力算法,它的核心思想是:从下标为 0 处比较主串和子串,若相等,则依次向下比较,直到子串结束,则得到匹配结果,若不相等,则主串回溯到下标为 1 处和子串下标为 0 处比较,依次类推,直到得到结果。
优点:比较容易理解,没有对主串和子串进行多余的处理。
缺点:每次每次字符不匹配时,都要回溯到上次比较的下一个位置,时间开销大。
大家可以按照下面的图来理解比较容易一些。
由此我们发现 i 每次走的位置都等于 j 走的位置,所以我们的到 i 拖回的位置为 i-j+1。
下面用 java 实现 BF 算法的代码。
public static int BF(String str,String sub,int pos){ //1.判断pos合法性。 if(pos < 0 || pos > str.length()){ return -1; } //2.开始查找。 int i = pos; int j = 0; //3.遍历主串和子串。 while(i <str.length() && j <sub.length()){ if(str.charAt(i) == sub.charAt(j)){ i++; j++; }else{ i = i-j+1; j = 0; } } //4.哪种情况下表示找到 if(j >= sub.length()){ return i-j; }else{ return -1; } }
下面是运算结果:
public static void main(String[] args) { String str = "ababcabcdabcde"; String sub = "abcd"; System.out.println(BF(str,sub,0)); }
2.KMP算法
相比于 BF 算法,KMP 算法时间复杂度大大的减小了,主要在于主串不用回溯。但是 KMP 算法非常难理解,需要仔细琢磨。
我们举例来看。
以下是根据KMP算法写出的代码:
public static int Kmp(String str,String sub,int pos){ int i = pos; int j = 0; int[] next = new int[sub.length()]; getNext(next,sub); while(i < str.length() && j < sub.length()){ if(j == -1 || str.charAt(i) == sub.charAt(j)){ i++; j++; }else{ j = next[j]; } } if(j >= sub.length()){ return i-j; }else{ return -1; } }
那么我们如何来找 j 退回的位置呢,这才是 KMP 算法的核心所在。找到 j 的退回位置有如下规则:
找到匹配成功部分的两个相等的真子串(不包含本身),一个以下标 0 开始,另一个以 j-1 下标结尾。
不管什么数据 next[0] = -1;next[1] = 0;
例如上图:主串前面的 ab 和子串开头的 ab 相等,所以我们直接将 j 退回到 c 的位置,然后比较 i 和 j 位置的字符。
为了得到每个 j 返回的位置,我们需要设一个数组 next 来存储 j 返回的位置。
上图的子串对应的数组即为-1,0,0,0,1,2.
我们推导出如下的公式:
下面是根据这个规则写出的代码:
public static void getNext(int[] next,String str){ next[0] = -1; next[1] = 0; int i = 2;//下一项 int k = 0;//前一项的k while(i < str.length()){ if(k == -1||str.charAt(k) == str.charAt(i - 1)){//Pk == Pi时 next[i] = k + 1; i++; k = k+1; }else{ k = next[k]; } } }
如果有什么不当之处请指出。