Rabin-Karp 字符串哈希算法可用于解决字符串匹配问题,通过比较字符串的哈希而不是内容来减少复杂度
在该方法中,我们将字符串看成一个 base 进制的数,它对应的十进制值就是哈希值。显然,两个字符串的哈希值相等,当且仅当这两个字符串本身相同。然而如果字符串本身很长,其对应的十进制值在大多数语言中无法使用内置的整数类型进行存储。因此,我们会将十进制值对一个大质数 mod 进行取模。此时:
- 如果两个字符串的哈希值在取模后不相等,那么这两个字符串本身一定不相同;
- 如果两个字符串的哈希值在取模后相等,并不能代表这两个字符串本身一定相同。例如两个字符串的哈希值分别为 2 和 15,模数为 13,虽然 2 %15 = 15 (mod 13),但它们不相同。
一般来说,我们选取一个大于字符集大小(即字符串中可能出现的字符种类的数目)的质数作为 base,再选取一个在字符串长度平方级别左右的质数作为 mod,产生哈希碰撞的概率就会很低。需要注意,这种方法并不严谨,不应用于实际工程中。
以leetcode 214. 最短回文串 为例
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
示例 1:
输入: "aacecaaa"
输出: "aaacecaaa"
示例 2:
输入: "abcd"
输出: "dcbabcd"
算法思路:
要找到最短回文串,其实就是找到字符串s中的最长前缀回文串s1
而一个字符串是回文串,当且仅当该字符串与它的反序相同。因此,我们可以暴力地枚举 s1的结束位置,并计算 s1与 s1的反序的哈希值。如果这两个哈希值相等,说明我们找到了一个 s 的前缀回文串。字符串和其反序的hash公式如下:
代码:
class Solution {
public String shortestPalindrome(String s) {
int n = s.length();
int base = 131, mod = 1000000007;
int left = 0, right = 0, mul = 1;
int best = -1;
for (int i = 0; i < n; ++i) {
left = (int) (((long) left * base + s.charAt(i)) % mod);
right = (int) ((right + (long) mul * s.charAt(i)) % mod);
if (left == right) {
best = i;
}
mul = (int) ((long) mul * base % mod);
}
String add = (best == n - 1 ? "" : s.substring(best + 1));
StringBuffer ans = new StringBuffer(add).reverse();
ans.append(s);
return ans.toString();
}
}