问题描述
我正在解决Leetcode 1044,答案是使用二进制搜索和滚动哈希。基本上使用二进制搜索来选择长度,然后搜索该长度的重复字符串。在这里,滚动散列发挥了作用,以节省空间(而不是使用集合存储所有子串,而是存储子串的散列)。这就是解决方案的背景。
我的问题是用于防止溢出的模数。我选择了Long.MAX_VALUE,我相信它足以应付它,但当我使用Long.MAX_VALUE时,答案是不正确的。但是,当我使用Long.MAX_VALUE / 26或Math.pow(2,32)时,它们都可以工作。抱歉,我对模数很不好,我想我肯定错过了一些事情。有人可以阐明它吗?谢谢!以下是我的解决方案:
public static long modulus = Long.MAX_VALUE / 26;
public String longestDupSubstring(String S) {
int n = S.length();
int l = 1;
int r = n - 1;
int index = -1;
while (l <= r) {
int m = l + (r - l) / 2;
int temp = findDuplicate(S,m);
if (temp != -1) {
index = temp;
l = m + 1;
}
else {
r = m - 1;
}
}
return index == -1 ? "" : S.substring(index,index + r);
}
private int findDuplicate(String s,int len) {
Set<Long> set = new HashSet<>();
long hash = 0;
long p = 1;
for (int i = 0; i < len; i++) {
hash = (hash * 26 + s.charAt(i) - 'a') % modulus;
p = (p * 26) % modulus;
}
set.add(hash);
for (int i = len; i < s.length(); i++) {
hash = (hash * 26 + (s.charAt(i) - 'a')
- (s.charAt(i - len) - 'a') * p) % modulus;
if (hash < 0) {
hash += modulus;
}
if (set.contains(hash)) {
return i - len + 1;
}
set.add(hash);
}
return -1;
}
解决方法
26不是模数的一部分,是哈希的一部分。如果我们在算法中将它们分开,那么我们可能会看到它是如何工作的。对于模数,通常很多就足够了,而不必是long
:
public final class Solution {
int a = 26;
int mod = 1 << 29;
public final String longestDupSubstring(
final String s
) {
int lo = 1;
int hi = s.length() - 1;
while (lo <= hi) {
int mid = lo + ((hi - lo) >> 1);
int startIndex = search(s,mid);
if (startIndex == - 1) {
hi = mid - 1;
}
else {
lo = -~mid;
}
}
int startIndex = search(s,hi);
return startIndex == -1 ? "" : s.substring(startIndex,startIndex + hi);
}
public final int search(
final String s,final int len
) {
long h = 0;
long aL = 1;
for (int i = 0; i < len; i++) {
h = (h * a % mod + s.charAt(i)) % mod;
aL = aL * a % mod;
}
HashMap<Long,List<Integer>> visited = new HashMap<>();
visited.put(h,new ArrayList<Integer>());
visited.get(h).add(0);
for (int i = 1; i < -~s.length() - len; i++) {
h = ((h * a % mod - s.charAt(i - 1) * aL % mod + mod) % mod + s.charAt(i + len - 1)) % mod;
if (visited.containsKey(h)) {
for (int start : visited.get(h)) {
if (s.substring(start,start + len).equals(s.substring(i,i + len))) {
return i;
}
}
} else {
visited.put(h,new ArrayList<Integer>());
}
visited.get(h).add(i);
}
return -1;
}
}