JAVA程序设计:最长公共子路径(LeetCode:1923)

本文介绍了一种解决关于n个城市和m个朋友旅游路径问题的方法,通过二分查找和滚动哈希技术,计算朋友们的最长公共子路径长度。实例演示了如何运用此算法来找出不同路径中的公共部分,并提供了代码实现和关键步骤的详细解析。
摘要由CSDN通过智能技术生成

一个国家由 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 。
 

提示:

1 <= n <= 105
m == paths.length
2 <= m <= 105
sum(paths[i].length) <= 105
0 <= paths[i][j] < n
paths[i] 中同一个城市不会连续重复出现。


思路:二分答案+滚动hash。

代码思路详见:https://leetcode-cn.com/problems/longest-common-subpath/solution/zui-chang-gong-gong-zi-lu-jing-by-leetco-ypip/

class Solution {

    private static int mod1 = 1000000007;
    private static int mod2 = 1000000009;
    private static int base1, base2;

    public int longestCommonSubpath(int n, int[][] paths) {
        int ans = 0;
        int m = paths.length;
        int len = 1000005;
        Random random = new Random();
        base1 = random.nextInt(10000000 - 1000000 + 1) + 1000000;
        base2 = random.nextInt(10000000 - 1000000 + 1) + 1000000;
        System.out.println(base1 + " " + base2);
        for (int i = 0; i < m; i++)
            len = Math.min(len, paths[i].length);
        int l = 1, r = len;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (check(n, paths, mid)) {
                ans = mid;
                l = mid + 1;
            } else
                r = mid - 1;
        }
        return ans;
    }

    private static boolean check(int n, int[][] arr, int len) {
        int m = arr.length;
        long mul1 = 1, mul2 = 1;
        for (int i = 1; i <= len; i++) {
            mul1 = mul1 * base1 % mod1;
            mul2 = mul2 * base2 % mod2;
        }
        Set<Long> s = new HashSet<>();
        for (int i = 0; i < m; i++) {
            long hash1 = 0, hash2 = 0;
            for (int j = 0; j < len; j++) {
                hash1 = (hash1 * base1 + arr[i][j]) % mod1;
                hash2 = (hash2 * base2 + arr[i][j]) % mod2;
            }
            Set<Long> t = new HashSet<>();
            long val = hash(hash1, hash2);
            if (i == 0 || s.contains(val))
                t.add(val);
            for (int j = len; j < arr[i].length; j++) {
                hash1 = ((hash1 * base1 % mod1 - (long) arr[i][j - len] * mul1 % mod1 + arr[i][j]) % mod1 + mod1) % mod1;
                hash2 = ((hash2 * base2 % mod2 - (long) arr[i][j - len] * mul2 % mod2 + arr[i][j]) % mod2 + mod2) % mod2;
                val = hash(hash1, hash2);
                if (i == 0 || s.contains(val))
                    t.add(val);
            }
            if (t.isEmpty())
                return false;
            s.clear();
            for (Long ss : t)
                s.add(ss);
        }
        return true;
    }

    private static long hash(long hash1, long hash2) {
        long val1 = String.valueOf(hash1).hashCode();
        long val2 = String.valueOf(hash2).hashCode();
        return val1 << 16 ^ val2;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值