📅  最后修改于: 2023-12-03 15:11:17.560000             🧑  作者: Mango
Rabin-Karp 算法是一种基于哈希值的字符串匹配算法,用于在一个主串中找到一个模式串的出现位置。该算法可以与朴素算法(暴力枚举)和 KMP 算法等常用算法相比,在某些情况下具有更优的时间复杂度。
以下是一个使用 Java 语言实现的 Rabin-Karp 算法的程序。程序接受两个字符串参数,分别为主串和模式串,返回模式串在主串中的起始位置。如果模式串不存在于主串中,则返回 -1。
public class RabinKarp {
// 使用的素数,用于哈希值计算,建议使用不大于 int 型最大值的最大素数
private static final int PRIME = 31;
public static int search(String text, String pattern) {
int n = text.length(), m = pattern.length();
if (n < m) {
return -1;
}
// 蒙特卡罗算法中使用的哈希值常数,这里任意定义
int hashConstant = 5643;
// 模式串哈希值
int patternHash = 0;
// 主串当前比较位置的哈希值
int currentHash = 0;
// 累乘因子
int factor = 1;
// 计算模式串哈希值和第一个比较位置的哈希值
for (int i = 0; i < m; ++i) {
patternHash = patternHash * PRIME + pattern.charAt(i);
currentHash = currentHash * PRIME + text.charAt(i);
if (i > 0) {
factor *= PRIME;
}
}
// 逐个比较
for (int i = 0; i <= n - m; ++i) {
if (patternHash == currentHash && pattern.equals(text.substring(i, i + m))) {
return i;
}
if (i < n - m) {
currentHash = (currentHash - text.charAt(i) * factor) * PRIME + text.charAt(i + m);
}
}
return -1;
}
}
本程序使用了一个常数作为模式串哈希值的计算因子,这是蒙特卡罗算法的一个优化。如果不需要蒙特卡罗算法可以去掉这个因子,否则需要在程序中找到一个合适的随机常数。
另外程序中采用了 Java 内置的 String.substring
方法来计算主串中的一个子串,这可能会造成一定程度的效率损失。如果需要优化可以考虑使用 char 数组等方式来获得子串。
以下是一个使用 JUnit 5 测试 Rabin-Karp 算法的测试样例。
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class RabinKarpTest {
@Test
void testSearch() {
assertEquals(2, RabinKarp.search("hello", "ll"));
assertEquals(-1, RabinKarp.search("hello", "llr"));
assertEquals(0, RabinKarp.search("hello", "he"));
assertEquals(3, RabinKarp.search("hello", "lo"));
assertEquals(2, RabinKarp.search("hello", "ll"));
assertEquals(-1, RabinKarp.search("", ""));
assertEquals(-1, RabinKarp.search("hello", ""));
assertEquals(0, RabinKarp.search("", ""));
}
}
Rabin-Karp 算法可以很好地解决模式搜索问题,其时间复杂度为 O(n+m)。在某些特定场景下它比暴力枚举和 KMP 等算法更快,但也有可能比它们慢。为了保证程序的性能建议在实际应用中对比几种算法的效率并选择最优的算法。