介绍
用传统的暴力匹配的方法进行字符串匹配的时间复杂度太大,效率往往不高。KMP算法减少了很多不必要的比较操作,通过已经匹配主串的前缀中的最长可匹配后缀和模式串中的最长可匹配前缀使得模式串的快速移动。时间复杂度可达O(n + m) 【n,m分别是主串和模式串的长度】
next数组
算法的关键是找到主串最长可匹配后缀和模式串中的最长可匹配前缀,由于前面的部分是已经匹配的,我们可以直接在模式串中生成next数组。下标指的是j位置不匹配,j位置前的子串都匹配。next存的是最长可匹配前缀串的长度,也即最长可匹配前缀的下一个位置。比如ABAB就是2, ABABA就是3。
在找到nxt数组之后,每次如果在模式串的j位置匹配失败,我们只需要让j = next[j],这样在模式串之前的部分是可以和主串匹配的最长前缀,然后继续重复这个过程,直到匹配成功或失败
代码
public class KMP {
public static void main(String[] args) {
String str1 = "ATCGAGAGAAGCTTGCATGCAGTGCA";
String str2 = "GAGAAGCT";
System.out.println(kmp(str1, str2));
}
static int kmp(String str, String pattern){
int[] nxt = getNext(pattern);
//i和j分别是主串和模式串的下标
int j = 0;
for (int i = 0; i < str.length(); i++) {
while (j > 0 && str.charAt(i) != pattern.charAt(j)){
j = nxt[j];
}
if (str.charAt(i) == pattern.charAt(j)){
++j;
}
if (j == pattern.length()){
return i - pattern.length() + 1;
}
}
return -1;
}
static int[] getNext(String pattern){
int [] nxt = new int[pattern.length()];
int tail = 2, head = 0;
for (; tail< pattern.length(); tail++) {
while (head != 0 && pattern.charAt(head) != pattern.charAt(tail - 1)){
head = nxt[head];
}
if (pattern.charAt(head) == pattern.charAt(tail - 1)){
++head;
}
nxt[tail] = head;
}
return nxt;
}
}