题目地址:
https://leetcode.com/problems/longest-duplicate-substring/
给定一个长度大于等于 2 2 2的字符串 s s s,求其最长重复子串。最长重复子串是指长度小于 s s s并且出现次数超过 1 1 1的子串。如果不存在则返回空串。
这题可以用后缀数组,再求 s s s的height数组。这里采用一个简单做法,二分 + 字符串哈希。显然答案的长度是 [ 1 , l s − 1 ] [1,l_s-1] [1,ls−1],我们可以二分答案,对于每个二分出来的长度 x x x,我们找 s s s中是否有重复出现过的长度是 x x x的子串。可以先对 s s s做前缀哈希,这样可以 O ( 1 ) O(1) O(1)的时间判断某个子串是否之前出现过。代码如下:
class Solution {
public:
using UL = unsigned long;
string longestDupSubstring(string s) {
int n = s.size();
int ss = 0, sl = 0;
auto check = [&](int len) {
UL ha = 0, P = 131, pow = 1;
for (int i = 0; i < len; i++) {
ha = ha * P + s[i];
pow = pow * P;
}
unordered_set<UL> st{ha};
for (int i = len; i < n; i++) {
ha = ha * P + s[i];
ha -= pow * s[i - len];
if (st.count(ha)) {
ss = i - len + 1, sl = len;
return true;
}
st.insert(ha);
}
return false;
};
int l = 0, r = n - 1;
while (l < r) {
int mid = l + (r - l + 1 >> 1);
if (check(mid)) l = mid;
else r = mid - 1;
}
return s.substr(ss, sl);
}
};
时间复杂度 O ( n log n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)。