求一个字符串(模式串)在另一个字符串(主串)中的位置,称为字符串模式匹配
1. 朴素模式匹配
对主串 S 和模式串 T 分别设置指针 i 和 j ,假设字符串下标从 0 开始,初始时 i 和 j 分别指向每个串的第 0 个位置。在第 n 趟匹配开始时,i 指向主串 S 中的第 n-1 个位置,j 指向模式串T的第0个位置,然后逐个向后比较。若 T 中的每一个字符都与 S 中的字符相等,则称匹配成功,否则,当遇到某个字符不相等时,i 重新指向 S 的第 n 个位置,j 重新指向 T 的第 0 个位置,继续进行第 n+1 趟匹配。
时间复杂度为 O(n*m),其中 n 和 m 分别为主串和模式串的长度。
/**
* @param source 主串
* @param pattern 模式串
* @param pos 主串中开始的位置
* @return 匹配成功返回模式串在主串从pos开始的位置,匹配失败,返回-1
*/
public static int index(String source,String pattern,int pos){
// 主串中的位置
int i= pos;
// 模式串中位置
int j = 0;
char[] src = source.toCharArray();
char[] ptn = pattern.toCharArray();
while (i<src.length && j<ptn.length){
if (src[i] == ptn[j]){
// 当前字符匹配成功
i++;
j++;
}else {
i = i - j +1;
j = 0;
}
}
if (j == ptn.length){
return i - j;
}else
return -1;
}
2. KMP算法
next 数组求解算法:
对于模式串 T,next[j] 代表了 T 的前 j 个字符组成的子串中,其前缀和后缀的最长公共串的长度。
- next[0] = -1, next[1]=0。
- 在求解 next[j] 时,令 k=next[j-1],
- 比较 T[j-1] 与 T[k] 的值,
a. 若 T[j-1] 等于 T[k],则 next[j]=k+1。
b. 若 T[j-1] 不等于 T[k],令k=next[k],若k等于-1,则 next[j]=0,否则跳至3。
public class KMP {
/**
* 获取部分匹配表
*/
private static int[] getNext(String pattern){
char[] ptn = pattern.toCharArray();
int[] next = new int[pattern.length()];
// 第一位置为-1,第二个位置为0
next[0] = -1;next[1] = 0;
int k = 0;
for (int j = 2; j < ptn.length; j++) {
k = next[j-1];
while (k != -1){
if (ptn[j-1] == ptn[k]){
next[j] = k+1;
break;
}else {
k = next[k];
}
// 当 k==-1而跳出循环时,next[j] = 0
next[j] = 0;
}
}
return next;
}
/**
* 进行KMP 模式匹配
* @param src 主串
* @param pattern 模式串
* @return 匹配成功返回模式串在主串中的第一次出现的位置
* 匹配失败,返回 -1
*/
public static int KMPMatch(String src,String pattern){
char[] S = src.toCharArray();
char[] T = pattern.toCharArray();
int next[] = getNext(pattern);
/**
* i: 主串中的位置
* j:模式串中的位置
*/
int i = 0,j = 0;
while (i<S.length && j<T.length){
if (j == -1 || S[i] == T[j]){
i++;
j++;
}else {
j = next[j];
}
}
if (j == T.length){
return i-j;
}else {
return -1;
}
}
public static void main(String[] args){
String src = "ababaaaba";
String pattern = "aa";
int index = KMPMatch(src,pattern);
System.out.println(index);
}
}