基本思想
字符串匹配问题:在文本串中寻找是否有与模式串相同的子串
在字符串匹配时,暴力匹配忽略了如果模式串开头与中间有重复部分,在将模式串的后面的重复部分匹配后,无法继续匹配的话,此时将模式串开头实际上也已经被匹配了,可以直接从开头重复部分之后开始匹配
这里引入了一个部分匹配值表,每个与模式串的字符对应,表示与开头的字符有没有相同的,如果有,表的值就是相同字符的下一位,但只从开头开始对应
这样就使用了匹配失败的结果,从而减少了匹配次数
算法实现
public static int kmpSearch(String s1,String s2)
{
//获得部分匹配值表
int[] next = kmpNext(s2);
//遍历
//i是s1的角标,j是s2的角标
for(int i = 0, j = 0; i < s1.length(); i++)
{
//i与j角标元素不等,将s2右移到重复位置
while(j > 0 && s1.charAt(i) != s2.charAt(j) )
j = next[j - 1];
if(s1.charAt(i) == s2.charAt(j))
j++;
if(j == s2.length())
return i- j + 1;
}
return -1;
}
//获取部分匹配值表
private static int[] kmpNext(String s)
{
//初始化匹配值表
int[] next = new int[s.length()];
//第一个匹配值为0
next[0] = 0;
//i用来遍历所有字符,初始值为1与j不同,j表示其匹配表下标
for(int i = 1, j = 0; i < next.length; i++)
{
//i角标与j角标元素不等,将
while(j > 0 && s.charAt(i) != s.charAt(j) )
j = next[j - 1];
//i角标与j角标元素相等,对双方下一位进行判断
if(s.charAt(i) == s.charAt(j) )
j++;
//赋予匹配值
next[i] = j;
}
return next;
}
测试
public static void main(String[] args) {
String s1 = "BBC ABCDAB ABCDABCDABDE";
String s2 = "ABCDABD";
System.out.println(kmpSearch(s1, s2));
}
结果为
15
算法是有效的
算法分析
设s1有n个字符,s2有m个字符
- 匹配过程要遍历s1数组,时间复杂度为O(n)
- next数组要遍历s2数组,时间复杂度为O(m)
- 总时间复杂度为O(n+m)