这个题解使用了后缀数组的思想。主要步骤如下:
1. 通过 SA-IS 算法构建后缀数组 sa 和 rk 数组。sa[i] 表示将所有后缀排序后第 i 小的后缀的起始位置,rk[i] 表示后缀 s[i:] 在 sa 中的排名。
2. 通过 sa 和 rk 计算 height 数组。height[i] 表示 sa[i] 和 sa[i-1] 这两个后缀的最长公共前缀(LCP)的长度。
3. 在 height 数组中找到最大值 mh,则最长重复子串长度为 mh,起始位置为 sa[height.index(mh)]。
时间复杂度: O(n)
空间复杂度: O(n)
def inducedSort(s: List[int], sa1: List[int], t: List[int], m=26):
n = len(s)
cnt = [0] * m
for i in range(n):
cnt[s[i]] += 1
for i in range(1, m):
cnt[i] += cnt[i - 1]
start, end = [0] + cnt[:-1], cnt[:]
sa = [-1] * n + [n]
for x in reversed(sa1[1:]):
cnt[s[x]] -= 1
sa[cnt[s[x]]] = x
for i in range(-1, n):
if sa[i] > 0:
c = sa[i] - 1
if not t[c]:
sa[start[s[c]]] = c
start[s[c]] += 1
for i in range(n - 1, -1, -1):
if sa[i] > 0:
c = sa[i] - 1
if t[c]:
end[s[c]] -= 1
sa[end[s[c]]] = c
return sa[:-1]
def SA_IS(s: List[int], m=26):
n = len(s)
t = [False] * (n + 1)
for i in reversed(range(n - 1)):
t[i] = t[i + 1] if s[i] == s[i + 1] else (s[i] < s[i + 1])
critical = list()
for i in range(1, n):
if t[i] and not t[i - 1]:
critical.append(i)
nc = len(critical)
index = [-1] * n + [n]
for i, x in enumerate(critical):
index[x] = i
sa0 = inducedSort(s, [n] + critical, t, m)
s1 = [0] * (nc + 1)
critical.append(n)
last, p, repeat = "", 0, False
for x in sa0:
if index[x] >= 0:
c = s[x : critical[index[x] + 1]]
if c != last:
p += 1
last = c
else:
repeat = True
s1[index[x]] = p
if repeat:
sa1 = [critical[x] for x in SA_IS(s1, p + 1)]
else:
sa1 = [n] + [x for x in sa0 if index[x] >= 0]
return inducedSort(s, sa1, t, m)
def suffixArray(s: str) -> (List[int], List[int], List[int]):
n, k = len(s), 0
sa = SA_IS([ord(x) - 97 for x in s]) # 构建后缀数组
rk = [0] * n # 初始化 rk 数组
for i in range(n):
rk[sa[i]] = i # 计算 rk 数组
height = [0] * n # 初始化 height 数组
s += '#' # 在字符串末尾添加一个特殊字符,以避免边界判断
for i in range(n):
if rk[i]: # 从 rk[i] > 0 的后缀开始计算 height
if k > 0:
k -= 1
while s[i + k] == s[sa[rk[i] - 1] + k]: # 计算 sa[rk[i]] 和 sa[rk[i]-1] 的 LCP
k += 1
height[rk[i]] = k
return rk, sa, height
class Solution:
def longestDupSubstring(self, s: str) -> str:
rk, sa, height = suffixArray(s)
mh = max(height) # 找到 height 数组中的最大值
start = sa[height.index(mh)] # 最长重复子串的起始位置
return s[start : start + mh] # 返回最长重复子串