简介
一种高效的字符串匹配算法,算法核心是减少了字符串匹配循环的趟数,字符串匹配本身是无法优化的。
思路
KMP算法分为两个核心部分,一部分是计算next数组,一部分是利用next数组对模式字符串和主字符串的匹配。
假如现在有主字符串
S:A C T G P A C T G K A C T G P A C Y
P:A C T G P A C Y
如何计算next数组:
next数组是针对模式串P而言的,next[i]对应的下标,为P[0…i-1]的最长公共前后缀的长度,令P[0]=-1。下面这个参考leetcode的官网示例,链接地址:力扣
next数组计算过程是这样,如何编程呢:
比如对于前缀字符串ACTGPAC而言,我们定义一个指向主串的下标j,当j等于6时,得到主串的前缀子串ACTGPAC。此时next[j]=1,意味着ACTGPAC的前一个前缀子串ACTGPA存在最长公共前后缀的长度为1,如果ACTGPAC的next下标值为2的话,那ACTGPAC的最后一位C必然和前一个前缀子串ACTGPA的第二位字符相同,如果不同则next下标值为0。
如何利用next数组对模式字符串和主字符串匹配:
从S的第一位开始逐个比较和P的字符,当比较到i位置时,发现二者不匹配,此时P的i位置的字符为Y,next数组值为2,那么这里就是将j置到P的下标为2的位置就是T,接下来从P的j位置开始和S的i位置进行比较,直到S的最后一位。
代码
public class Solution10 {
public int match(String p, String s){
int i = 0;
int j = 0;
int s_len = s.length();
int p_len = p.length();
int[] next = buildNext(p);
while(i < s_len && j < p_len){
if (j == -1 || s.charAt(i) == p.charAt(j)){
i++;
j++;
}
else {
j = next[j];
}
}
if (j == p_len){
return i - j;
}
else {
return -1;
}
}
// 构造模式串P的next表,这里其实就是给定的模式串自己和自己比较,找到真前缀和后缀最大公共部分
public int[] buildNext(String p){
int len_p = p.length();
// 定义next表
int[] N = new int[len_p];
N[0] = -1; // 方便编程
int k = -1; // 模式串的指针
int j = 0; // “主串”的指针
while (j < len_p - 1){ // 不包括模式串本身,所以最后一位不考虑
if (k == -1 || p.charAt(j) == p.charAt(k)){ // 当“主串”的指针对应的字符和模式串的指针对应的字符相同,N[j]的值等于k+1值
++k;
++j;
N[j] = k;
}else {
k = N[k];
}
}
return N;
}
public static void main(String[] args) {
Solution10 solution10 = new Solution10();
System.out.println(solution10.match("ACTGPACY", "ACTGPACTGKACTGPACY"));
}
}