Bingbong的回文路径
Bingbong的回文路径
题目描述
Bingbong 有一棵有根树,根节点为 ,总共有 个节点。树中的节点通过 条无向边相连,每条边的权重为 。
树中的每个节点包含一个小写字母。Bingbong 将选择从节点 开始,并在选择最短路径的情况下到达节点 。他想知道他所走路径形成的字符串是否是一个回文串。由于他会进行多次行走,您需要回答多个查询。
输入描述:
第一行包含一个整数 ,表示节点的数量。
第二行包含 个小写字母,其中第 个字母表示第 个节点上的小写字母。
第三行包含 个整数,其中第 个数字 表示第 个节点的父节点,对于根节点 。
第四行包含一个整数 ,表示查询的数量。
接下来是 行,每行包含两个整数 和 ,表示一个查询的起点和终点。
输出描述:
输出 行,每行包含一个字符串。如果查询的起点到终点的路径连接形成一个回文串,则输出 "YES",否则输出 "NO"。
示例1
输入
5
bbaaa
0 1 1 2 2
3
2 3
4 3
5 3
输出
NO
YES
YES
解题思路
假设 和 的最近公共祖先是 ,从 走到 再走到 的字符串表示为 ,同理从 走到 再走到 的字符串表示为 。当 时说明 到 路径上的字符串是回文串。判断回文串可以用字符串哈希(这里基数 取 ,自然溢出取模),由于是树上的问题我们可以通过倍增来维护树链的信息。
定义 表示从 往上走 步后达到的节点, 表示从 往上走 步构成字符串的哈希值(不含 上的字符), 表示从 往下走 步构成字符串的哈希值(含 上的字符)。以下图为例:
从 往上走 步构成的字符串为 acbc
,从 往下走 步构成的字符串为 cbca
。
考虑如何维护这些信息。对于 ,我们先从 往上走 步到达 ,再从 往上走 步走到 ,因此有 。
对于两个字符串 和 ,哈希值分别为 和 ,那么拼接后的 的哈希值就是 。因此 ,。
对于询问的 ,我们可以仿照求最近公共祖先的做法,分别求出 和 对应字符串的哈希值。在 往上跳到 的过程中,维护 表示从 往上跳的过程中构成字符串的哈希值,和 表示往下跳到 的过程中构成字符串的哈希值。同理维护 的 和 。最后让 跳到 , 跳到 的下一个节点,判断是否满足 即可,其中 表示 往上跳经过的节点个数, 表示 往上跳经过的节点个数。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ULL;
const int N = 1e5 + 5, M = N * 2, P = 13331;
char s[N];
int h[N], e[M], ne[M], idx;
int fa[N][17], dep[N];
ULL h1[N][17], h2[N][17], pp[N];
void add(int u, int v) {
e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}
void dfs(int u, int p) {
dep[u] = dep[p] + 1;
fa[u][0] = p;
h1[u][0] = h2[u][0] = s[p];
for (int i = 1; i <= 16; i++) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
h1[u][i] = h1[u][i - 1] * pp[1 << i - 1] + h1[fa[u][i - 1]][i - 1];
h2[u][i] = h2[fa[u][i - 1]][i - 1] * pp[1 << i - 1] + h2[u][i - 1];
}
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (v == p) continue;
dfs(v, u);
}
}
bool query(int a, int b) {
if (dep[a] < dep[b]) swap(a, b);
ULL ha1 = s[a], ha2 = s[a], hb1 = s[b], hb2 = s[b];
int d1 = 1, d2 = 1;
for (int i = 16; i >= 0; i--) {
if (dep[fa[a][i]] >= dep[b]) {
ha1 = ha1 * pp[1 << i] + h1[a][i];
ha2 = h2[a][i] * pp[d1] + ha2;
d1 += 1 << i;
a = fa[a][i];
}
}
if (a == b) return ha1 == ha2;
for (int i = 16; i >= 0; i--) {
if (fa[a][i] != fa[b][i]) {
ha1 = ha1 * pp[1 << i] + h1[a][i];
ha2 = h2[a][i] * pp[d1] + ha2;
d1 += 1 << i;
a = fa[a][i];
hb1 = hb1 * pp[1 << i] + h1[b][i];
hb2 = h2[b][i] * pp[d2] + hb2;
d2 += 1 << i;
b = fa[b][i];
}
}
// 当前a和b往上跳1步就会同时到达最近公共祖先,选择让a跳
ha1 = ha1 * pp[1] + h1[a][0];
ha2 = h2[a][0] * pp[d1++] + ha2;
return ha1 * pp[d2] + hb2 == hb1 * pp[d1] + ha2;
}
int main() {
int n, m;
scanf("%d %s", &n, s + 1);
memset(h, -1, sizeof(h));
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
add(i, x), add(x, i);
}
pp[0] = 1;
for (int i = 1; i <= n; i++) {
pp[i] = pp[i - 1] * P;
}
dfs(1, 0);
scanf("%d", &m);
while (m--) {
int a, b;
scanf("%d %d", &a, &b);
printf("%s\n", query(a, b) ? "YES" : "NO");
}
return 0;
}
参考资料
牛客小白月赛91文字版题解:https://ac.nowcoder.com/discuss/1294108
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18147822
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-04-20 最长公共子串