该题解采用后缀数组和LCP数组结合滑动窗口的方法来寻找最长公共子路径。首先,构建一个由所有路径组成的超级字符串,每个路径之间用一个特殊的分隔符隔开。然后对这个超级字符串构建后缀数组,这样就可以在O(n log n)的时间内找到所有后缀的排序。接下来,使用后缀数组计算出最长公共前缀(LCP)数组。利用LCP数组,通过滑动窗口技术来维护所有路径在当前窗口内的最小LCP值,确保窗口内包含所有路径至少一次。最后,窗口的最小LCP值即为我们要找的最长公共子路径的长度。
时间复杂度: O(n log n)
空间复杂度: O(n)
def inducedSort(s, sa1, t, m):
# 数组s表示待排序的数字,sa1表示已排序的部分,t是类型数组,m是桶的数量
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 suffixArray(s, bucketNum):
# 构建后缀数组
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 = [i for i in range(1, n) if t[i] and not t[i - 1]]
index = [-1] * n + [n]
for i, x in enumerate(critical):
index[x] = i
sa0 = inducedSort(s, [n] + critical, t, bucketNum)
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 suffixArray(s1, p + 1)]
else:
sa1 = [n] + [x for x in sa0 if index[x] >= 0]
return inducedSort(s, sa1, t, bucketNum)
def getHeight(s, sa):
# 计算LCP数组
n, k = len(s), 0
rk = [0] * n
for i in range(n):
rk[sa[i]] = i
height = [0] * n
s += [-1]
for i in range(n):
if rk[i]:
if k > 0:
k -= 1
while s[i + k] == s[sa[rk[i] - 1] + k]:
k += 1
height[rk[i]] = k
return height, rk
class Solution:
def longestCommonSubpath(self, n, paths):
# 寻找最长公共子路径
label, s = [], []
m = len(paths)
for i, path in enumerate(paths):
label += [i] * (len(path) + 1)
s += [x + m for x in path]
s.append(m-i)
sa = suffixArray(s, n + m)
height, _ = getHeight(s, sa)
height[0] = float('inf')
cnt = [0] * m
p, cntcnt, ans = 0, 0, 0
q = deque()
for i, x in enumerate(sa):
cl = label[x]
cnt[cl] += 1
if cnt[cl] == 1:
cntcnt += 1
while q and q[-1][0] > height[i]:
q.pop()
q.append((height[i], i))
while cnt[label[sa[p]]] > 1:
if q[0][1] == p + 1:
q.popleft()
cnt[label[sa[p]]] -= 1
p += 1
if cntcnt == m:
c = q[0][0]
if c > ans:
ans = c
return ans