最近在学字符串匹配算法,也算搞懂了一点点,在这里将我写的代码汇总一下。
这里的代码被我改成了LeetCode的题目格式,读者有兴趣的话可以去过一下这道题。
leetcode28. 实现 strStr()
class Solution {
public int strStr(String haystack, String needle) {
if(haystack == null || needle == null) {
return -1;
}
if(needle.isEmpty()) return 0;
if(haystack.isEmpty()) return -1;
return KR_Match(haystack, needle);
}
//KMP算法
int KMP_Match(String T, String P) {
int n = T.length(), i = 0;
int m = P.length(), j = 0;
//构建next表
int[] next = new int[m];
int t = next[0] = -1;
while(j < m - 1) {
if(t < 0 || P.charAt(j) == P.charAt(t)) {
t++;
j++;
next[j] = P.charAt(j) == P.charAt(t) ? next[t] : t;
} else {
t = next[t];
}
}
//开始进行匹配
j = 0;
while(i < n && j < m) {
if(j < 0 || T.charAt(i) == P.charAt(j)) {
i++;
j++;
} else {
j = next[j];
}
}
//若i - j > n - m则代表找不到对应的子串
return i - j > n - m ? -1 : i - j;
}
//BF算法
int BF_Match(String T, String P) {
int n = T.length(), i = 0;
int m = P.length(), j = 0;
while(i < n && j < m) {
if(T.charAt(i) == P.charAt(j)) {
i++;
j++;
} else {
i -= (j - 1);
j = 0;
}
}
return i - j > n - m ? -1 : i - j;
}
//BM算法
int BM_Match(String T, String P) {
int n = T.length();
int m = P.length();
//构造BC表
int[] BC = new int[256];
for(int i = 0; i < 256; i++) {
BC[i] = m;
}
for(int i = 0; i < m - 1; i++) {
BC[P.charAt(i)] = m - i - 1;
}
//构造suffix表
int[] suffix = new int[m];
suffix[m - 1] = m;
for(int i = m - 2; i >= 0; i--) {
int q = i;
while(q >= 0 && P.charAt(q) == P.charAt(m - 1 - i + q)) {
--q;
}
suffix[i] = i - q;
}
//构造GS表
int[] GS = new int[m];
for(int i = 0; i < m; i++) {
GS[i] = m;
}
int j = 0;
for(int i = m - 1; i >= 0; i--) {
if(suffix[i] == i + 1) {
for(; j < m - 1 - i; j++) {
GS[j] = m - 1 - i;
}
}
}
for(int i = 0; i < m - 1; i++) {
GS[m - 1 - suffix[i]] = m - 1 - i;
}
//开始匹配
int i = 0;
j = 0;
while(j <= n - m) {
for(i = m - 1; i >= 0 && P.charAt(i) == T.charAt(i + j); i--);
if(i < 0) return j;//i < 0代表匹配完毕,也就是找到了,可以直接返回j
else j += Math.max(GS[i], BC[T.charAt(i + j)] - m + 1 + i);
}
return -1;
}
//Karp-Rabin算法
int KR_Match(String T, String P) {
int n = T.length();
int m = P.length();
int MOD = 1000000;
int power = 1;
for(int i = 0; i < m; i++) {
power = (power * 31) % MOD;
}
//计算出模式串的哈希值,31是个经验值,更不容易冲突
int HashP = 0;
for(int i = 0; i < m; i++) {
HashP = (HashP * 31 + P.charAt(i)) % MOD;
}
int HashT = 0;
for(int i = 0; i < n; i++) {
//计算文本串中与模式串长度相同的子串的哈希值
HashT = (HashT * 31 + T.charAt(i)) % MOD;
//当i < m - 1时,即第一个子串长度小于m时,让其继续计算
if(i < m - 1) {
continue;
}
//当子串长度超出m时,丢掉最前面的字符的哈希值
if(i >= m) {
HashT = (HashT - T.charAt(i - m) * power) % MOD;
if(HashT < 0) { //丢掉最前面的哈希值之后,可能变为负数,这时直接加上MOD就行了
HashT += MOD;
}
}
//两个串的哈希值相等
if(HashT == HashP){
//但是两个串并不一定完全相等,可能存在冲突,通过equals方法再比较
if(P.equals(T.substring(i - m + 1, i + 1))) {
return i - m + 1;
}
}
}
return -1;
}
}