最长重复子串
难度:
标签:
题目描述
代码结果
运行时间: 362 ms, 内存: 21.0 MB
/*
* Problem: Find the longest duplicate substring in a given string using Java Streams.
*
* Approach:
* 1. Utilize binary search to determine the length of the longest duplicate substring.
* 2. Use the Stream API and a hash set to find duplicates efficiently.
* 3. The Rabin-Karp rolling hash algorithm is used for hashing substrings.
*/
import java.util.*;
import java.util.stream.*;
public class SolutionStream {
public String longestDupSubstring(String s) {
int left = 1, right = s.length();
String result = "";
while (left < right) {
int mid = left + (right - left) / 2;
String dup = findDuplicateUsingStream(s, mid);
if (dup != null) {
result = dup;
left = mid + 1;
} else {
right = mid;
}
}
return result;
}
private String findDuplicateUsingStream(String s, int length) {
Set<Long> seen = new HashSet<>();
long base = 26, mod = (long) Math.pow(2, 32);
return IntStream.range(0, s.length() - length + 1)
.mapToObj(i -> s.substring(i, i + length))
.mapToLong(sub -> sub.chars().mapToLong(c -> c - 'a').reduce(0, (acc, c) -> (acc * base + c) % mod))
.filter(hash -> !seen.add(hash))
.mapToObj(hash -> s.substring(seen.stream().mapToInt(seen::hashCode).boxed().collect(Collectors.toList()).indexOf(hash),
seen.stream().mapToInt(seen::hashCode).boxed().collect(Collectors.toList()).indexOf(hash) + length))
.findFirst().orElse(null);
}
}
解释
方法:
这个题解使用了后缀数组的思想。主要步骤如下:
1. 通过 SA-IS 算法构建后缀数组 sa 和 rk 数组。sa[i] 表示将所有后缀排序后第 i 小的后缀的起始位置,rk[i] 表示后缀 s[i:] 在 sa 中的排名。
2. 通过 sa 和 rk 计算 height 数组。height[i] 表示 sa[i] 和 sa[i-1] 这两个后缀的最长公共前缀(LCP)的长度。
3. 在 height 数组中找到最大值 mh,则最长重复子串长度为 mh,起始位置为 sa[height.index(mh)]。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
在构建后缀数组时,为什么选择SA-IS算法而不是其他算法如后缀树或Burrows-Wheeler变换?
▷🦆
后缀数组sa、排名数组rk和最长公共前缀数组height之间有什么具体的关系和作用?
▷🦆
为什么在计算height数组时需要在字符串末尾添加一个特殊字符`#`?
▷🦆
在计算最长公共前缀(LCP)时,变量k的作用是什么,为什么初始化时k为0,并在每次循环时递减k?
▷