题目链接:https://leetcode-cn.com/problems/longest-duplicate-substring/
题号:1044
题目描述:
给出一个字符串 S,考虑其所有重复子串(S 的连续子串,出现两次或多次,可能会有重叠)。 返回任何具有最长可能长度的重复子串。(如果 S 不含重复子串,那么答案为 ""。)。
示例1:
输入:"banana" 输出:"ana"
示例2:
输入:"abcd" 输出:""
题目分析:
leetcode上大佬说的:
我用具体的例子解释一下: “因此我们可以使用二分查找的方法,找到最大的 L “ 这个"因此"不太显然我觉得。首先明确,二分查找的目的是:选取子串长度L,然后交给Rabin-Karp计算是否存在长度有L的重复子串,如果存在,则尝试长度更长的子串。 我懒得写题解,就用序号啦,将就看吧各位。 以题解中子任务一的示意图为例,当前串为“leetcodecookies”,长度为15,用两个指针left=1,right=15指向首尾。
(这里先提一下,left不仅仅是一个指针,二分结束时还可以通过Left得知最终的找到的最大长度l,这个地方没懂不要紧,看了下面再回过来看也行。) 通过(left+right)/2计算mid值为8,则通过Rarbin-Karp(代码中对应(search()函数)计算是否有长度为8的重复子串。
显然没有,即search函数返回-1,说明8比最长重复子串的长度长。所以需要缩小这个长度,我们让right=mid=8,
再重新计算mid,看到这里应该能感觉出来,mid值其实就是二分查找不断测试的长度值,就是“选取”。 再计算之后mid=(1+8)/2=4,再交给search计算,search返回值仍然是-1,所以再让right=mid,再计算mid=2,此时交给seach函数返回值不为1。
说明存在长度为2的重复子串,此时应该移动left,来使得选取的mid值增大,以查看比2长的重复字串是否存在。 令left=mid+1=3,再计算mid=(3+4)=3,再交给search,返回值为-1,不存在长度为3的重复子串,让right=mid=3,此时left=right,while终止。 所以最长重复子串长度为2,怎么计算这个长度呢?答案是left - 1。因为left的值一定是上一个成功检测到重复的mid值+1。
代码:
c++:
leetcode:(超时)
PTA:
#pragma warning(disable:4996) #include <iostream> #include <string> #include <unordered_map> using namespace std; string a; string ans = ""; int search(int r, int l) { string b; unordered_map<string, int>hs; int len = r - 1, flag; while (l + len <= a.length()) { b = a.substr(l, len); if (!hs[b]) { hs[b] = l; l++; } else { len++; while (l + len < a.length() && a[hs[b] + len] == a[l + len]) { len++; } flag = len - 1; b = a.substr(l, len - 1); ans = b; if (l + len < a.length()) { string c = a.substr(hs[b], len); string d = a.substr(l, len); hs[c] = hs[b]; hs[d] = l; l++;//继续遍历,找出是否还有长度大于len的子串 } else return flag; } } if (ans.empty()) return 0; else return flag; } int main() { getline(cin, a); a.insert(0, "!"); int r = a.length() - 1; int l = 1; int flag = 1; while (l != r) { int mid = l + (r - l + 1) / 2; if ((flag = search(mid, flag)) && flag != 0) break; else r = mid - 1; } cout << flag << " " << ans; } /* qwertyuqwertdfqwerty */