1044. 最长重复子串
给出一个字符串 S,考虑其所有重复子串(S 的连续子串,出现两次或多次,可能会有重叠)。
返回任何具有最长可能长度的重复子串。(如果 S 不含重复子串,那么答案为 “”。)
示例 1:
输入:“banana” 输出:“ana”
示例 2:
输入:“abcd” 输出:“”
提示:
- 2 <= S.length <= 10^5
- S 由小写英文字母组成。
思路:
考虑到数据范围,应该是O(nlogn)级别的时间复杂度,先考虑能否二分,对于本题,如果长度为k的子串有重复,那么任何长度小于k的子串也一定重复。
即存在最长重复子串,长度小于等于该长度的子串都有重复,但长度大于它的子串不会重复,故可以使用二分。
第二个问题,如何在每个二分中使得时间复杂度为O(n),即如何快速找出该长度的子串是否有重复?
使用哈希表 + 字符串哈希,字符串哈希保证了O(n)的时间复杂度,哈希表用于辅助记录以出现过的子串的哈希值。
本题帮助复习了字符串哈希的细节
class Solution {
long[] h = new long[100010], pow = new long[100010];
final int P = 131;
int ans = -1;
public String longestDupSubstring(String s) {
int n = s.length();
pow[0] = 1;
for (int i = 1; i <= n; i++) {
h[i] = h[i - 1] * P + s.charAt(i - 1);
pow[i] = pow[i - 1] * P;
}
int l = 1, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(s, mid))
l = mid;
else
r = mid - 1;
}
check(s, l);
if (ans == -1)
return "";
return s.substring(ans - 1, ans + l - 1);
}
boolean check(String s, int len) {
Set<Long> set = new HashSet();
for (int i = len; i <= s.length(); i++) {
long a = get(i - len + 1, i);
if (set.contains(a)) {
ans = i - len + 1;
return true;
}
else
set.add(a);
}
return false;
}
long get(int l, int r) {
return h[r] - h[l - 1] * pow[r - l + 1];
}
}
718. 最长重复子数组
思路:
方法1:类似于最长公共子序列,只是换成了连续而已
方法2:字符串哈希 + 二分
// DP
class Solution {
public int findLength(int[] nums1, int[] nums2) {
int n = nums1.length, m = nums2.length;
int[][] f = new int[n + 1][m + 1];
int res = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (nums1[i - 1] == nums2[j - 1])
f[i][j] = f[i - 1][j - 1] + 1;
res = Math.max(f[i][j], res);
}
}
return res;
}
}
// 方法2
class Solution {
long[] pow = new long[1010];
public int findLength(int[] nums1, int[] nums2) {
int n = nums1.length, m = nums2.length;
long x = 0, p = 13331;
pow[0] = 1;
for (int i = 1; i <= 1000; i++)
pow[i] = p * pow[i - 1];
long[] a1 = new long[n + 1];
long[] a2 = new long[m + 1];
for (int i = 1; i <= n; i++) {
a1[i] = a1[i - 1] * p + (nums1[i - 1] + 1);
}
for (int i = 1; i <= m; i++) {
a2[i] = a2[i - 1] * p + (nums2[i - 1] + 1);
}
int l = 0, r = Math.min(n, m);
while (l < r) {
int mid = l + r + 1 >> 1;
Set<Long> set = new HashSet<>();
for (int i = mid; i <= n; i++)
set.add(get(a1, i - mid + 1, i));
boolean flag = false;
for (int j = mid; j <= m; j++)
if (set.contains(get(a2, j - mid + 1, j))) {
flag = true;
break;
}
if (flag) l = mid;
else r = mid - 1;
}
return l;
}
long get(long[] a, int l, int r) {
return a[r] - (a[l - 1] * pow[r - l + 1]);
}
}