标签: 字符串 二分查找 动态规划 后缀数组 哈希函数 滚动哈希
难度: Medium
标签: 字符串 二分查找 动态规划 后缀数组 哈希函数 滚动哈希
难度: Medium
运行时间: 37 ms
内存: 0.0 MB
class Solution: def longestRepeatingSubstring(self, s: str) -> int: left = 0 right = len(s) def haveRepetition(mid): hSet = set() for i in range(len(s) - mid + 1): temp = s[i:i+mid] if temp in hSet: return True hSet.add(temp) return False while left < right: middle = (left + right + 1) // 2 if haveRepetition(middle): left = middle else: right = middle - 1 return right
这个题解采用了二分搜索的方法来寻找最长重复子串的长度。算法的基本思想是,通过二分搜索在可能的子串长度范围内查找,利用一个辅助函数 `haveRepetition` 来检查给定长度 `mid` 的子串是否在字符串 `s` 中重复出现。如果有重复,增大查找范围的下限 `left`;如果没有重复,减小查找范围的上限 `right`。最终,`right` 指向的是最长的重复子串的长度。
时间复杂度: O(n log n)
空间复杂度: O(n)
class Solution: def longestRepeatingSubstring(self, s: str) -> int: # 初始化二分搜索的左右边界 left = 0 right = len(s) # 辅助函数,用于检测长度为 mid 的子串是否有重复 def haveRepetition(mid): hSet = set() for i in range(len(s) - mid + 1): temp = s[i:i+mid] if temp in hSet: return True hSet.add(temp) return False # 二分搜索最长重复子串的长度 while left < right: middle = (left + right + 1) // 2 if haveRepetition(middle): left = middle else: right = middle - 1 return right
在二分搜索中,目标是确定可以找到重复子串的最长长度。当`haveRepetition`函数确定存在长度为`mid`的重复子串时,意味着所有小于或等于`mid`的长度都可能有重复子串。因此,为了找到更长的可能重复子串,需要将下限`left`调整为`mid`,从而在更大的长度范围内继续搜索。相反,如果没有重复子串,这表明长度为`mid`及以上的子串都不会重复,所以将上限`right`调整为`mid - 1`,在更小的长度范围内继续寻找。
在`haveRepetition`函数中,通过遍历从0到`len(s) - mid`的所有可能起始位置,并截取长度为`mid`的子串,检查这些子串是否在哈希集合`hSet`中已经存在。如果存在,则表示发现了重复子串;如果不存在,则将该子串加入到集合中。这种方法通过覆盖所有可能的起始位置,确保了不会漏掉任何可能的重复子串。
虽然在理论上,当字符串非常长时,存储大量子串可能会消耗大量内存,但在实际应用中,通常操作系统和编程语言的内存管理机制能够有效地处理这种情况。此外,二分搜索迅速缩小检查长度的范围,减少了需要同时存储的子串数量。如果内存仍是问题,可以考虑使用更高效的数据结构(如滚动哈希)来减少存储需求。
使用`(left + right + 1) // 2`而不是`(left + right) // 2`是为了在二分搜索中防止死循环和确保能够覆盖所有可能的情况。特别是当`left`和`right`非常接近时,`(left + right) // 2`可能会导致`middle`值偏小,从而可能在某些情况下无法正确调整`left`的值,造成死循环。使用`(left + right + 1) // 2`确保`middle`在必要时能够取得更大的值,从而更有效地探索搜索空间。