news 2026/8/7 0:49:04

DeepSeek LeetCode 3841. 查询树上回文路径 Java实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeepSeek LeetCode 3841. 查询树上回文路径 Java实现

解题思路

这道题的核心在于如何高效判断树中任意两点路径上的字符能否重排为回文串。

回文串的判定条件:一个字符串能重排成回文串,当且仅当其出现奇数次的字符最多只有一个。例如 "aac" 中 a 出现2次(偶),c 出现1次(奇),可重排为 "aca"。

核心优化技巧——前缀异或(Prefix XOR)与位掩码:

· 用26位整数(int)的二进制位表示每个字符的奇偶性。某位为1表示对应字符出现奇数次,0表示偶数次。
· 定义 mask[node] 为从根节点到该节点路径上所有字符的奇偶掩码。
· 树上两点 u 和 v 之间路径的奇偶掩码计算公式为:
mask(u→v) = mask(u) XOR mask(v) XOR (1 << char(lca(u, v)))
其中 lca(u, v) 是 u 和 v 的最近公共祖先。

处理更新操作:节点字符变更时,只需更新以该节点为根的整棵子树的 mask 值。利用 DFS序(欧拉序) 将子树转化为连续区间,再用 树状数组(Fenwick Tree) 维护区间异或和与单点查询。

---

Java 实现代码

```java
import java.util.*;

public class Solution {
// 链式前向星存图
private int[] head, to, nxt;
// 二进制提升(LCA)
private int[][] up;
private int[] depth;
// DFS序(欧拉序)
private int[] in, out;
private int timer;
private int maxLog;
// 树状数组
private BIT bit;
// 当前字符数组
private char[] chars;

public List<Boolean> palindromePath(int n, int[][] edges, String s, String[] queries) {
chars = s.toCharArray();
// 1. 建图
buildGraph(n, edges);

// 2. DFS预处理:深度、父节点、DFS序
maxLog = 31 - Integer.numberOfLeadingZeros(n);
up = new int[maxLog + 1][n];
depth = new int[n];
in = new int[n];
out = new int[n];
timer = 0;
dfs(0, -1);

// 3. 二进制提升表
for (int k = 1; k <= maxLog; k++) {
for (int i = 0; i < n; i++) {
up[k][i] = up[k - 1][up[k - 1][i]];
}
}

// 4. 树状数组维护每个节点的前缀掩码
bit = new BIT(n + 2);
for (int i = 0; i < n; i++) {
int mask = 1 << (chars[i] - 'a');
bit.rangeXor(in[i], out[i], mask);
}

// 5. 处理查询
List<Boolean> ans = new ArrayList<>();
for (String query : queries) {
if (query.startsWith("update")) {
// 解析:update ui c
int space1 = query.indexOf(' ');
int space2 = query.indexOf(' ', space1 + 1);
int u = Integer.parseInt(query.substring(space1 + 1, space2));
char c = query.charAt(space2 + 1);
if (c != chars[u]) {
int oldMask = 1 << (chars[u] - 'a');
int newMask = 1 << (c - 'a');
int diff = oldMask ^ newMask; // 变化的位
bit.rangeXor(in[u], out[u], diff);
chars[u] = c;
}
} else {
// 解析:query u v
int space1 = query.indexOf(' ');
int space2 = query.indexOf(' ', space1 + 1);
int u = Integer.parseInt(query.substring(space1 + 1, space2));
int v = Integer.parseInt(query.substring(space2 + 1));
int l = lca(u, v);
// 路径掩码 = mask(u) ^ mask(v) ^ char(lca)
int mask = bit.pointQuery(in[u]) ^ bit.pointQuery(in[v]) ^ (1 << (chars[l] - 'a'));
// 判断是否只有0个或1个1
ans.add((mask & (mask - 1)) == 0);
}
}
return ans;
}

// ---------- 建图 ----------
private void buildGraph(int n, int[][] edges) {
int m = edges.length;
head = new int[n];
Arrays.fill(head, -1);
to = new int[m * 2];
nxt = new int[m * 2];
for (int i = 0; i < m; i++) {
int u = edges[i][0], v = edges[i][1];
to[i * 2] = v;
nxt[i * 2] = head[u];
head[u] = i * 2;
to[i * 2 + 1] = u;
nxt[i * 2 + 1] = head[v];
head[v] = i * 2 + 1;
}
}

// ---------- DFS:深度、父节点、DFS序 ----------
private void dfs(int u, int parent) {
in[u] = ++timer;
up[0][u] = parent == -1 ? 0 : parent;
for (int e = head[u]; e != -1; e = nxt[e]) {
int v = to[e];
if (v == parent) continue;
depth[v] = depth[u] + 1;
dfs(v, u);
}
out[u] = timer;
}

// ---------- LCA:二进制提升 ----------
private int lca(int u, int v) {
if (depth[u] < depth[v]) {
int tmp = u; u = v; v = tmp;
}
// 提升u到与v同深度
int diff = depth[u] - depth[v];
for (int k = maxLog; k >= 0; k--) {
if ((diff & (1 << k)) != 0) {
u = up[k][u];
}
}
if (u == v) return u;
for (int k = maxLog; k >= 0; k--) {
if (up[k][u] != up[k][v]) {
u = up[k][u];
v = up[k][v];
}
}
return up[0][u];
}

// ---------- 树状数组(支持区间异或、单点查询) ----------
static class BIT {
int n;
int[] tree;

BIT(int n) { this.n = n; tree = new int[n + 1]; }

void add(int idx, int val) {
for (; idx <= n; idx += idx & -idx) tree[idx] ^= val;
}

// 区间 [l, r] 异或上 val
void rangeXor(int l, int r, int val) {
add(l, val);
add(r + 1, val);
}

// 单点查询
int pointQuery(int idx) {
int res = 0;
for (; idx > 0; idx -= idx & -idx) res ^= tree[idx];
return res;
}
}
}
```

代码解释

1. dfs预处理:计算每个节点的深度、父节点和 DFS 进入/退出时间戳。同一子树的节点在 in 和 out 之间形成连续区间。
2. BIT 树状数组:维护每个节点对应的前缀奇偶掩码(从根到该节点)。rangeXor(in[u], out[u], mask) 将 u 的整棵子树所有节点的前缀掩码异或上 mask。
3. 查询处理:
· 用 pointQuery(in[u]) 获取 mask(u)。
· 计算路径掩码:mask(u) ^ mask(v) ^ (1 << char(lca))。
· 判断 (mask & (mask - 1)) == 0,即二进制中是否只有0个或1个1。
4. 更新处理:字符从 old 变为 new 时,diff = (1<<old) ^ (1<<new) 表示变化的位,对 u 的子树区间异或 diff 即可。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/7 0:45:44

如何选择优质的网站建设招标方案以打造高转化率数字化营销入口并避开隐形陷阱

说实话,写这篇文章之前,我反复掂量了很久。因为在互联网行业摸爬滚打这么多年,我见过的因为一个错误的“网站建设招标方案”而让整个项目烂尾的案例,简直比过春节返乡堵车时加塞的车还多。很多老板或者是负责项目的同事,拿到招标文件的那一刻,心里想的往往是“赶紧发出去…

作者头像 李华
网站建设 2026/8/7 0:37:33

长沙3合1网站建设如何助力中小企业低成本实现数字化转型与高效获客全攻略

在这个移动互联网飞速迭代的时代,对于咱们长沙的中小企业主或者创业团队来说,最头疼的问题往往不是产品不够好,也不是服务不够硬,而是“酒香也怕巷子深”。以前大家觉得,开个店在繁华地段就好,只要人来了,生意自然少不了。但现在不一样了,大家的注意力都被手机屏幕截胡…

作者头像 李华
网站建设 2026/8/7 0:28:14

门户网站建设目标:如何构建真正具备商业价值与用户体验的数字化入口平台

在这个信息爆炸、流量为王的时代,每一个企业或品牌都在寻找属于自己的数字化阵地。当我们谈到“门户网站建设”时,很多人脑海中浮现的可能只是几个静态页面,或者是一个简单的公司新闻展示窗。但事实上,这种认知如果停留在表层,往往会让原本可以成为核心竞争力的网站,沦为…

作者头像 李华
网站建设 2026/8/7 0:20:41

揭秘电子商务网站建设价格真相:避坑指南与隐形成本全解析

在开始深入探讨这个话题之前,我想先请大家闭上眼睛想象一下这样的场景:你是一位满怀热情的创业者,手里攥着一份精心打磨的商业计划书,心里盘算着如何利用互联网的东风让自己的产品飞向全国各地。你迫不及待地联系了几家所谓的“专业”建站公司,满心欢喜地期待着得到一个靠…

作者头像 李华
网站建设 2026/8/7 0:14:04

心理综评容易失分?常态化心理培育助力身心成长

心理综评容易失分&#xff1f;常态化心理培育助力身心成长在综评身心健康维度中&#xff0c;心理健康是极易被忽视的细分板块&#xff0c;很多学生只注重体育锻炼&#xff0c;完全忽视心理素养积累&#xff0c;导致该板块内容单薄、评级偏低&#xff0c;莫名丢失综评分数。常态…

作者头像 李华
网站建设 2026/8/7 0:12:20

郑州品牌网站建设怎么做才真正有用:揭秘中小企业如何通过官网提升转化率

在郑州,这座城市每天醒来的时候,空气中都弥漫着一种特有的躁动与生机。作为中原腹地的核心引擎,这里不仅有胡辣汤的辛辣和烩面的劲道,更有无数创业者日夜兼程的脚步声。如果你是一个在郑州深耕多年的企业老板,或者是一个刚起步的小微企业主,你一定会和我有过同样的困扰:…

作者头像 李华