牛客小白月赛91 ----- Bingbong的回文路径 ---- 题解

文章讨论了一种在给定一棵树结构中,通过预计算每个节点到任意节点的路径哈希值,快速判断两个节点之间的路径是否为回文字符串的问题。作者利用深度优先搜索(DFS)和字符串哈希技术解决了这个问题。
摘要由CSDN通过智能技术生成

Bingbong的回文路径:

题目描述:

思路解析:

现在有一棵树,树上每个结点上都有一个小写字母,那么如果唯一确定了x和y两个结点,那么就唯一确定了一个字符串路径(最短路径)。 -现在给出q次查询,问x和y这个路径是否是回文字符串。

一看到回文字符串就可以应当想到字符串hash。但是怎么快速查询这个路径上字符串的hash值变成为了关键,可以发现,根节点到到任意结点的路径是确定的,并且是可以在一次dfs遍历中维护出来根据点到任意结点的字符串路径hash值的,那我们检查这个值有什么用。

假设我们查询红色结点到蓝色结点,那么可以发现,我们只需要找到他们最小公共祖先就可以利用他们三个结点的hash值来维护任意结点之间的hash值了。那么这道题就可以做了

代码实现:

import java.util.*;


import java.io.*;

public class Main {
    static long[] pow = new long[100005];
    static long[] inv = new long[100005];
    public static void main(String[] args) throws IOException {
        Random random = new Random();
        for (int i = 0; i < 26; i++) {
            s[i] = random.nextInt(10000);
        }
        pow[0] = 1;
        inv[0] = 1;
        long invb = qkm(base);
        for (int i = 1; i < 100005; i++) {
            pow[i] = pow[i - 1] * base % mod;
            inv[i] = inv[i - 1] * invb % mod;
        }
        int t = 1;
        while (t > 0) {
            solve();
            t--;
        }
        w.flush();
        w.close();
    }
    static char[] str;
    static int[] s = new int[26];
    static int mod = (int) 1e9 + 9;
    static int base = 131;
    static Vector<Integer>[] g;
    static int[] dep;
    static int[][] st;

    static long[] pre;
    static long[] suf;
    public static void solve() throws IOException {
        int n = f.nextInt();
        str = (" " + f.next()).toCharArray();
        g = new Vector[n+1];
        dep = new int[n+1];
        st = new int[n+1][20];
        pre = new long[n+1];
        suf = new long[n+1];
        for (int i = 0; i < n + 1; i++) {
            g[i] = new Vector<>();
        }
        for (int i = 1; i <= n; i++) {
            int x = f.nextInt();
            g[x].add(i);
        }
        dfs(1, 0);
        for (int j = 1; j < 20; j++) {
            for (int i = 1; i <= n; i++) {
                st[i][j] = st[st[i][j-1]][j-1];
            }
        }

        int q = f.nextInt();
        for (int i = 0; i < q; i++) {
            int x = f.nextInt(); int y = f.nextInt();
            if (check(x,y)) w.println("YES");
            else w.println("NO");
        }
    }
    public static boolean check(int x, int y){
        int fa = lca(x, y);
        long a = (pre[x] - (pre[fa] * pow[dep[x] - dep[fa]] % mod)) % mod;
        long b = (pre[y] - (pre[fa] * pow[dep[y] - dep[fa]] % mod)) % mod;
        long c = (suf[x] - suf[fa]) * inv[dep[fa]] % mod;
        long d = (suf[y] - suf[fa]) * inv[dep[fa]] % mod;
        a = (a + mod) % mod;
        b = (b + mod) % mod;
        c = (c + mod) % mod;
        d = (d + mod) % mod;
        long A = (s[str[fa] - 'a'] * pow[dep[x] - dep[fa]] % mod + a + d * pow[dep[x] - dep[fa] + 1] % mod) % mod;
        long B = (s[str[fa] - 'a'] * pow[dep[y] - dep[fa]] % mod + b + c * pow[dep[y] - dep[fa] + 1] % mod) % mod;
        return A == B;
    }

    public static int lca(int x, int y){
        if (dep[x] < dep[y]) {int tmp = x; x = y; y = tmp;}
        for (int i = 19; i >= 0; i--) {
            if (dep[st[x][i]] >= dep[y]){
                x = st[x][i];
            }
        }
        if (x == y) return x;
        for (int i = 19; i >= 0; i--) {
            if (st[x][i] != st[y][i]){
                x = st[x][i]; y = st[y][i];
            }
        }
        return st[x][0];
    }
    public static long qkm(long a){
        long res = 1;
        long b = mod - 2;
        while (b > 0){
            if ((b & 1) == 1) res = (res * a) % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return res;
    }
    public static void dfs(int x, int fa){
        dep[x] = dep[fa] + 1;
        st[x][0] = fa;
        pre[x] = (pre[fa] * base % mod+ s[str[x]-'a']) % mod;
        suf[x] = (suf[fa] + s[str[x]-'a'] * pow[dep[fa]] % mod) % mod;
        for (int i = 0; i < g[x].size(); i++) {
            int y = g[x].get(i);
            if (y == fa) continue;
            dfs(y, x);
        }
    }


    static PrintWriter w = new PrintWriter(new OutputStreamWriter(System.out));
    static Input f = new Input(System.in);

    static class Input {
        public BufferedReader reader;
        public StringTokenizer tokenizer;

        public Input(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }

        public String next() throws IOException {

            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                tokenizer = new StringTokenizer(reader.readLine());
            }
            return tokenizer.nextToken();
        }

        public String nextLine() throws IOException {
            String str = null;
            str = reader.readLine();
            return str;
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }

        public long nextLong() throws IOException {
            return Long.parseLong(next());
        }

        public Double nextDouble() throws IOException {
            return Double.parseDouble(next());
        }
    }
}

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Studying~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值