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;elser = 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;}elseset.add(a);}return false;}long get(int l, int r) {return h[r] - h[l - 1] * pow[r - l + 1];}}
718. 最长重复子数组
思路:
方法1:类似于最长公共子序列,只是换成了连续而已
方法2:字符串哈希 + 二分
// DPclass 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;}}
// 方法2class 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]);}}
