字符串匹配指在主串字符串s,中找到模式串t。
字符串匹配指在主串字符串s,中找到模式串t。
BF算法
BF算法是朴素的字符匹配算法,思想是:如果模式串t和主串s,在对应位置匹配那么下标同时增加1,否则模式串从头开始,回到上次匹配中主串开始位置的下一位置继续往后比较,直到主串末尾。可以看到其时间复杂度为O(m*n)。
KMP算法
kmp在bf算法上进行了改进,主要是在模式串每次都要从头开始与主串匹配这一过程进行了改进。
假设t从0到j-1 与 s从i-j到i-1匹配,在j和i出处失配,见下图
如果t串的T0-Tj-1 不等于 T1-Tj(红色线所示) 那么
t串回溯时按照bf算法,回溯到S(i-j+1)处,T(0)-T(j) (细红线所示)必然不会与 S(i-j+1)到S(i)匹配,因为红叉部分与S的绿色部分匹配了,这样造成了不必要的回溯。
同理,回溯到S(i-j+2)如果满足 t串的T0-T(j-2) 不等于 T2-Tj,t和s也是失配的
那么只有找到一个k 值满足 T(0)-T(k-1) 等于 T(j-k)-T(j-1)时说明T串可以直接回溯到k位置,此时满足S(i-k)-S(i-1) 等于T(0)-T(k-1),可以直接从T(k)处和S(i)进行匹配。绿色线部分,所以减少了不必要的回溯,提高效率。
kmp算法的思想:
s和t,下标从都从0开始,字符匹配则i,j都加1,否则,j就回溯到上面提到的k处,i不变。继续比较,匹配i+1,j+1,否则j继续重复回溯到此时j对应的k处,不断重复。。。。。直到,(1).j回溯时的k与Si匹配则i,j增加1,(2)j回退到j==-1,此时i,j增加1,S(i+1)和T0比较。
那么,重要的就是如何求得k的值了,k的值只跟模式串自身有关,我们将S(j)对应的k存储在next(j)数组中,
k满足 模式串t 中 k之前 k个字符 与 T(j)之前的 k个字符相等。j是与主串在i处失配的位置这也得到,i之前k个字符与t的k位置之前k个字符相等。
下面是java的kmp算法实现。
//获取next[]数组
public static int[] getNext(String pattern) {
int j = 0;// pattern 的j位置
int k = -1;// k的初值
int[] next = new int[pattern.length()];
next[0] = -1;// j==0 时 k ==-1
while (j < pattern.length() - 1) {
if (k == -1 || pattern.charAt(j) == pattern.charAt(k)) {
j++;
k++;
next[j] = k;
} else {
k = next[k];// k 取上次的k值
}
System.out.println("j=" + j + ",k=" + next[j]);
}
return next;
}
//kmp算法求匹配的下标
public static int KMPIndex(String s, String t) {
int i = 0, j = 0;
int[] next = getNext(t);
while (i < s.length() && j < t.length()) {
if (j == -1 || s.charAt(i) == t.charAt(j)) {
i++;
j++;
} else {
j = next[j];// i 不变,j回溯到k
}
}
if (j >= t.length()) {
return i - t.length(); // s中匹配到了t
} else {
return -1;// 未匹配到 t
}
}
//测试
public class KMP {
public static void main(String[] args) {
String s = "ababcabcacbab";
String t = "abcac";
System.out.println("匹配到t的位置为:" + KMPIndex(s, t));
}
public static int[] getNext(String pattern) {
int j = 0;// pattern 的j位置
int k = -1;// k的初值
int[] next = new int[pattern.length()];
next[0] = -1;// j==0 时 k ==-1
while (j < pattern.length() - 1) {
if (k == -1 || pattern.charAt(j) ==pattern.charAt(k)) {
j++;
k++;
next[j] = k;
} else {
k = next[k];// k 取上次的k值
}
System.out.println("j=" + j + ",k=" + next[j]);
}
return next;
}
public static int KMPIndex(String s, String t) {
int i = 0, j = 0;
int[] next = getNext(t);
while (i < s.length() && j < t.length()) {
if (j == -1 || s.charAt(i) == t.charAt(j)) {
i++;
j++;
} else {
j = next[j];// i 不变,j回溯到k
}
}
if (j >= t.length()) {
return i - t.length(); // s中匹配到了t
} else {
return -1;// 未匹配到 t
}
}
}
结果
j=1,k=0
j=1,k=0
j=2,k=0
j=2,k=0
j=3,k=0
j=4,k=1
匹配到t的位置为:5
总结:kmp对bf算法进行了改进,主要是有模式串求出j对应的next数组,kmp算法的时间复杂度为O(m+n)。