1、KMP算法:
package com.arithmetic.stringprocessing;
//KMP算法,通过kmp方法在源字符串中查找模式字符串,并返回匹配的位置。
//computeLPS方法用于计算模式字符串的部分匹配表(Longest Proper Prefix which is also Suffix)。
//示例中,输入的目标字符串为text,模式字符串为pattern,输出结果为匹配的位置。如果匹配失败,返回-1。
public class KMPAlgorithmDemo {
public static int kmp(String text, String pattern) {
int n = text.length();
int m = pattern.length();
// 构建模式字符串的部分匹配表
int[] lps = computeLPS(pattern);
int i = 0; // 目标字符串的索引
int j = 0; // 模式字符串的索引
while (i < n) {
if (text.charAt(i) == pattern.charAt(j)) {
i++;
j++;
if (j == m) {
// 匹配成功,返回匹配位置
return i - j;
}
} else {
if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}
// 匹配失败,返回-1
return -1;
}
private static int[] computeLPS(String pattern) {
int m = pattern.length();
int[] lps = new int[m];
int len = 0; // 最长相等的前缀后缀长度
int i = 1;
while (i < m) {
if (pattern.charAt(i) == pattern.charAt(len)) {
len++;
lps[i] = len;
i++;
} else {
if (len != 0) {
len = lps[len - 1];
} else {
lps[i] = 0;
i++;
}
}
}
return lps;
}
public static void main(String[] args) {
String text = "ABABDABACDABABCABAB";
String pattern = "ABABCABAB";
int index = kmp(text, pattern);
if (index == -1) {
System.out.println("Pattern not found!");
} else {
System.out.println("Pattern found at index " + index);
}
}
}
2、Boyer-Moore算法:
package com.arithmetic.stringprocessing;
import java.util.Arrays;
//Boyer-Moore算法,通过boyerMoore方法在目标字符串中查找模式字符串,并返回匹配的位置。
//在实现中,使用badChar数组构建坏字符规则,使用suffix数组构建好后缀规则。
//输入的目标字符串text,模式字符串为pattern,输出结果为匹配的位置。如果匹配失败,返回-1。
public class BoyerMooreAlgorithmDemo {
private static final int NUM_OF_CHARS = 256;
public static int boyerMoore(String text, String pattern) {
int n = text.length();
int m = pattern.length();
if (m == 0) {
return 0;
}
int[] badChar = new int[NUM_OF_CHARS];
// 初始化坏字符表
Arrays.fill(badChar, -1);
for (int i = 0; i < m; i++) {
badChar[pattern.charAt(i)] = i;
}
int[] suffix = new int[m];
char[] patternChars = pattern.toCharArray();
computeSuffix(patternChars, suffix);
int i = 0; // 目标字符串的索引
while (i <= n - m) {
int j = m - 1;// 模式字符串的索引
while (j >= 0 && patternChars[j] == text.charAt(i + j)) {
j--;
}
if (j == -1) {
// 匹配成功,返回匹配位置
return i;
} else {
// 坏字符规则
int charIndex = text.charAt(i + j);
i += Math.max(1, j - badChar[charIndex]);
// 好后缀规则
if (j < m - 1) {
int x = j + 1;
int shift = 0;
if (suffix[x] != -1) {
shift = x - suffix[x] + 1;
} else {
while (x >= 0 && suffix[x] == -1) {
x--;
}
if (x >= 0) {
shift = x + 1;
}
}
i += shift;
}
}
}
// 匹配失败,返回-1
return -1;
}
private static void computeSuffix(char[] pattern, int[] suffix) {
int m = pattern.length;
suffix[m - 1] = m;
int f = 0; // 前缀的起始位置
for (int i = m - 2; i >= 0; i--) {
while (f > 0 && pattern[f] != pattern[i + 1]) {
f = suffix[f] - 1;
}
if (pattern[f] == pattern[i + 1]) {
f--;
}
suffix[i] = f;
}
}
public static void main(String[] args) {
String text = "ABAAABCD";
String pattern = "ABC";
int index = boyerMoore(text, pattern);
if (index == -1) {
System.out.println("Pattern not found!");
} else {
System.out.println("Pattern found at index " + index);
}
}
}
3、Rabin-Karp算法:
package com.arithmetic.stringprocessing;
import java.util.ArrayList;
import java.util.List;
//Rabin-Karp算法使用哈希函数来比较模式串和文本串的哈希值,进而确定是否需要比较子串的字符。
//这种方法能够在平均情况下减少比较次数,从而提高匹配效率。
public class RabinKarpAlgorithmDemo {
private static final int PRIME = 101;
private static final int RADIX = 256;
//rabinKarpSearch方法,主要算法逻辑在这个方法中。它接受文本字符串和模式串作为输入,并返回一个包含匹配位置的整数列表。
public static List<Integer> rabinKarpSearch(String text, String pattern) {
List<Integer> matches = new ArrayList<>();
int n = text.length();
int m = pattern.length();
long patternHash = calculateHash(pattern, m, PRIME);
long textHash = calculateHash(text, m, PRIME);
for (int i = 0; i <= n - m; i++) {
if (patternHash == textHash && isPatternMatch(text, pattern, i)) {
matches.add(i);
}
if (i < n - m) {
textHash = recalculateHash(text, i, i + m, textHash, m, PRIME);
}
}
return matches;
}
//calculateHash方法,用于计算字符串的哈希值。它采用Horner's rule和mod运算来避免大数运算。
private static long calculateHash(String str, int len, int prime) {
long hashValue = 0;
for (int i = 0; i < len; i++) {
hashValue = (hashValue * RADIX + str.charAt(i)) % prime;
}
return hashValue;
}
//recalculateHash方法,用于在滑动窗口中重新计算哈希值。它减去旧索引位置字符的贡献,并加上新索引位置字符的贡献。
//通过mod运算和处理负值来确保哈希值的范围在0到prime-1之间。
private static long recalculateHash(String str, int oldIndex, int newIndex, long oldHash, int patternLen, int prime) {
long newHash = (oldHash - (str.charAt(oldIndex) * (long) Math.pow(RADIX, patternLen - 1)) % prime) % prime;
newHash = (newHash * RADIX + str.charAt(newIndex)) % prime;
if (newHash < 0) {
newHash += prime;
}
return newHash;
}
//isPatternMatch方法:用于检查文本字符串中的子串是否与模式串匹配。
private static boolean isPatternMatch(String text, String pattern, int index) {
for (int i = 0; i < pattern.length(); i++) {
if (text.charAt(index + i) != pattern.charAt(i)) {
return false;
}
}
return true;
}
//main方法:定义一个示例,使用示例文本字符串和模式串来演示Rabin-Karp算法的使用。它输出匹配位置或提示未找到匹配。
public static void main(String[] args) {
String text = "ABABDABACDABABCABAB";
String pattern = "ABABCABAB";
List<Integer> matches = rabinKarpSearch(text, pattern);
if (matches.isEmpty()) {
System.out.println("No matches found");
} else {
System.out.println("Pattern found at indexes: " + matches);
}
}
}