最长公共子路径
难度:
标签:
题目描述
代码结果
运行时间: 1443 ms, 内存: 92.1 MB
/*
* Solution Approach with Java Stream:
* 1. Use binary search to find the maximum length of the common subpath.
* 2. For each possible length, check if there is a common subpath by maintaining a set of hash values.
* 3. Use streams to perform set operations for each path.
*/
import java.util.*;
import java.util.stream.Collectors;
public class SolutionStream {
public int longestCommonSubpath(int n, int[][] paths) {
int left = 1, right = Arrays.stream(paths).mapToInt(p -> p.length).min().getAsInt();
long mod = (1L << 61) - 1;
long base = 1000000007;
while (left <= right) {
int mid = left + (right - left) / 2;
if (hasCommonSubpath(paths, mid, mod, base)) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
private boolean hasCommonSubpath(int[][] paths, int length, long mod, long base) {
Set<Long> commonHashes = null;
for (int[] path : paths) {
Set<Long> currentHashes = new HashSet<>();
long hash = 0, power = 1;
for (int i = 0; i < length; i++) {
hash = (hash * base + path[i]) % mod;
power = (power * base) % mod;
}
currentHashes.add(hash);
for (int i = length; i < path.length; i++) {
hash = (hash * base + path[i] - path[i - length] * power % mod + mod) % mod;
currentHashes.add(hash);
}
if (commonHashes == null) {
commonHashes = currentHashes;
} else {
commonHashes.retainAll(currentHashes);
if (commonHashes.isEmpty()) return false;
}
}
return !commonHashes.isEmpty();
}
}
解释
方法:
该题解采用后缀数组和LCP数组结合滑动窗口的方法来寻找最长公共子路径。首先,构建一个由所有路径组成的超级字符串,每个路径之间用一个特殊的分隔符隔开。然后对这个超级字符串构建后缀数组,这样就可以在O(n log n)的时间内找到所有后缀的排序。接下来,使用后缀数组计算出最长公共前缀(LCP)数组。利用LCP数组,通过滑动窗口技术来维护所有路径在当前窗口内的最小LCP值,确保窗口内包含所有路径至少一次。最后,窗口的最小LCP值即为我们要找的最长公共子路径的长度。
时间复杂度:
O(n log n)
空间复杂度:
O(n)
代码细节讲解
🦆
在构建后缀数组的过程中,你如何处理每个路径之间的特殊分隔符,以确保这些分隔符不会与城市编号混淆?
▷🦆
算法中提到了使用滑动窗口来维护路径的最小LCP值,但具体如何初始化窗口,并确保窗口中始终包含所有路径至少一次?
▷🦆
为什么在计算LCP数组时需要在字符串末尾添加一个特殊字符(-1)?这对于结果有什么具体影响?
▷🦆
在使用后缀数组和LCP数组寻找最长公共子路径时,你如何确保不会将不同路径中的重复城市作为公共子路径的一部分?
▷