why
之所以写这篇博客,是因为字符串处理比较常见,字符串处理里面的字符串匹配问题也非常常见
KMP算法的性能在字符串匹配上是最优的
因此在字符串匹配的问题上,可尽量将问题转化为判断一个字符串是否是另一个字符串的子串,再采用KMP算法进行求解
what
KMP算法是一种空间换时间的做法,通过存储公共前缀后缀来快速的进行字符串匹配
假如目标字符串A是 a a a c a a a b d
模式字符串B是 a a a b
BM
如果是采用BM(暴力求解)的方式
会有两个指针i和j,i指向字符串A当前匹配的字符,j指向字符串B当前匹配的字符
因此根据暴力的方式匹配步骤为: i j
0 0
1 1
2 2
在匹配两个字符串下标3的字符时,会发现两个字符不相等,就会将字符串B整体向右移,得到
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
此时再重新匹配的话指针i从1开始匹配,步骤为
匹配步骤为: i j
0 0
1 1
在匹配两个字符串下标为2的字符时,会发现两个字符不相等,此时依旧将字符串B整体向右移动、i从2开始匹配
此时的时间复杂度为O(m*n)
KMP
采用KMP算法的话有两个东西必须要先理解,分别时前缀表和next数组
前缀表:
a 0(其最大公共前缀后缀为0)
a a 1(其最大公共前缀后缀为1,为a)
a a a 2(其最大公共前缀后缀为2,为aa)
a a a b 0(其最大公共前缀后缀为0)
求next,将前缀表中求得的最大公共前缀后缀数置于字符串底下,再整体向右边移动,第一位置-1,得:
a a a b
next:-1 0 1 2
操作流程如下:
- 假设目标字符串匹配到i,模式字符串匹配到j;
如果j = -1,或者当前字符匹配成功(A[i] == B[j]),则i++、j++,并且继续匹配下一个字符
如果j != -1 并且当前字符匹配失败(A[i] != B[j]),则i不变,j = next[j]。 - 相当于匹配失败的时候,模式串B相对于A移动了j - next[j]位,也就是说在匹配下标为3失败的时候,字符串会向右挪3 - 2 = 1 位,而指针i依旧保持不变继续匹配
移动前:
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
移动后:
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
由于i为3(不变),j为2(j = next(j)),因此对比后发现依然匹配不上,因此模式串B继续移动 2 - 1 = 1
移动前:
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
移动后:
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
由于i为3(不变),j为1(j = next(j)),因此对比后发现依然匹配不上,因此模式串B继续移动 1 - 0 = 1
移动前:
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
移动后:
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
由于i为3(不变),j为0(j = next(j)),因此对比后发现依然匹配不上,因此模式串B继续移动 0 - (-1) = 1
移动前:
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
移动后:
目标字符串A: a a a c a a a b d
模式字符串B: a a a b
由于j为-1,因此i为4(自增1),j为0(也自增1),因此对比后发现匹配上了
此时的时间复杂度为O(m+n)
HOW
算法的实现分为两步,第一是求next数组,第二是求KMP
public class Main {
public static void main(String[] args) {
char []test = {'a','b','c','d','a','b'};
int []result = getNextArray(test);
for (int i:result){
System.out.print(i+",");
}
}
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;
}
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;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。