列表

详情


1923. 最长公共子路径

一个国家由 n 个编号为 0 到 n - 1 的城市组成。在这个国家里,每两个 城市之间都有一条道路连接。

总共有 m 个编号为 0 到 m - 1 的朋友想在这个国家旅游。他们每一个人的路径都会包含一些城市。每条路径都由一个整数数组表示,每个整数数组表示一个朋友按顺序访问过的城市序列。同一个城市在一条路径中可能 重复 出现,但同一个城市在一条路径中不会连续出现。

给你一个整数 n 和二维数组 paths ,其中 paths[i] 是一个整数数组,表示第 i 个朋友走过的路径,请你返回 每一个 朋友都走过的 最长公共子路径 的长度,如果不存在公共子路径,请你返回 0 。

一个 子路径 指的是一条路径中连续的城市序列。

 

示例 1:

输入:n = 5, paths = [[0,1,2,3,4],
                     [2,3,4],
                     [4,0,1,2,3]]
输出:2
解释:最长公共子路径为 [2,3] 。

示例 2:

输入:n = 3, paths = [[0],[1],[2]]
输出:0
解释:三条路径没有公共子路径。

示例 3:

输入:n = 5, paths = [[0,1,2,3,4],
                     [4,3,2,1,0]]
输出:1
解释:最长公共子路径为 [0],[1],[2],[3] 和 [4] 。它们长度都为 1 。

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: int longestCommonSubpath(int n, vector<vector<int>>& paths) { } };

python3 解法, 执行用时: 1248 ms, 内存消耗: 40.1 MB, 提交时间: 2023-08-31 09:35:40

class Solution:
    def longestCommonSubpath(self, n: int, paths: List[List[int]]) -> int:
        # 根据正文部分的分析,我们选择的 mod 需要远大于 10^10
        # Python 直接选择 10^9+7 * 10^9+9 作为模数即可
        # 乘积约为 10^18,远大于 10^10
        mod = (10**9 + 7) * (10**9 + 9)

        # 本题中数组元素的范围为 [0, 10^5]
        # 因此我们在 [10^6, 10^7] 的范围内随机选取进制 base
        base = random.randint(10**6, 10**7)
        
        m = len(paths)
        # 确定二分查找的上下界
        left, right, ans = 1, len(min(paths, key=lambda p: len(p))), 0
        while left <= right:
            length = (left + right) // 2
            mult = pow(base, length, mod)
            s = set()
            check = True

            for i in range(m):
                hashvalue = 0
                # 计算首个长度为 len 的子数组的哈希值
                for j in range(length):
                    hashvalue = (hashvalue * base + paths[i][j]) % mod

                t = set()
                # 如果我们遍历的是第 0 个数组,或者上一个数组的哈希表中包含该哈希值
                # 我们才会将哈希值加入当前数组的哈希表中
                if i == 0 or hashvalue in s:
                    t.add(hashvalue)
                # 递推计算后续子数组的哈希值
                for j in range(length, len(paths[i])):
                    hashvalue = (hashvalue * base - paths[i][j - length] * mult + paths[i][j]) % mod
                    if i == 0 or hashvalue in s:
                        t.add(hashvalue)
                if not t:
                    check = False
                    break
                s = t
            
            if check:
                ans = length
                left = length + 1
            else:
                right = length - 1
        
        return ans

golang 解法, 执行用时: 212 ms, 内存消耗: 23.8 MB, 提交时间: 2023-08-31 09:35:26

func longestCommonSubpath(_ int, paths [][]int) (ans int) {
	a := []int{}
	minPathLen := int(1e9) // 二分右边界
	for _, p := range paths {
		minPathLen = min(minPathLen, len(p))
		a = append(a, 1e9) // 用一个不存在 paths 中的数拼接所有路径
		a = append(a, p...)
	}
	n, m := len(a), len(paths)

	// 标记每个元素属于哪条路径
	ids := make([]int, n)
	id := -1
	for i, v := range a {
		if v == 1e9 {
			id++
			ids[i] = m
		} else {
			ids[i] = id
		}
	}

	// 构建 a 的后缀数组和高度数组
	s := make([]byte, 0, n*4)
	for _, v := range a {
		s = append(s, byte(v>>24), byte(v>>16), byte(v>>8), byte(v))
	}
	// Go 自带后缀数组,由 SA-IS 算法实现,复杂度 O(n)
	_sa := *(*[]int32)(unsafe.Pointer(reflect.ValueOf(suffixarray.New(s)).Elem().FieldByName("sa").Field(0).UnsafeAddr()))
	sa := make([]int32, 0, n)
	for _, v := range _sa {
		if v&3 == 0 {
			sa = append(sa, v>>2)
		}
	}
	rank := make([]int, n)
	for i := range rank {
		rank[sa[i]] = i
	}
	height := make([]int, n)
	h := 0
	for i, rk := range rank {
		if h > 0 {
			h--
		}
		if rk > 0 {
			for j := int(sa[rk-1]); i+h < n && j+h < n && a[i+h] == a[j+h]; h++ {
			}
		}
		height[rk] = h
	}

	// 二分求答案
	return sort.Search(minPathLen, func(limit int) bool {
		limit++
		vis := make([]int, m)
		for i := 1; i < n; i++ {
			if height[i] < limit {
				continue
			}
			cnt := 0
			for st := i; i < n && height[i] >= limit; i++ {
				// 检查 sa[i] 和 sa[i-1]
				if j := ids[sa[i]]; j < m && vis[j] != st {
					vis[j] = st
					cnt++
				}
				if j := ids[sa[i-1]]; j < m && vis[j] != st {
					vis[j] = st
					cnt++
				}
			}
			if cnt == m {
				return false
			}
		}
		return true
	})
}

func min(a, b int) int { if a < b { return a }; return b }

上一题