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

📅 发布时间:2026/8/7 0:49:43
DeepSeek    LeetCode 3841. 查询树上回文路径 Python3实现
解题思路这道题要求高效判断树中任意两点路径上的字符能否重排为回文串。回文串判定条件一个字符串能重排成回文串当且仅当其出现奇数次的字符最多只有一个。核心优化技巧——位掩码 前缀异或· 用26位整数表示每个字符的奇偶性第i位为1表示字符i出现奇数次。· 定义pref[x]为从根节点到x路径上所有字符的奇偶掩码。· 树上两点u和v之间路径的奇偶掩码为mask(u→v) pref[u] ^ pref[v] ^ (1 char(lca(u, v)))处理更新操作节点字符变更时会影响以该节点为根的整棵子树所有节点的pref值。利用DFS序欧拉序将子树转化为连续区间[tin[u], tout[u]]再用树状数组Fenwick Tree维护区间异或更新与单点查询。---Python3 实现代码pythonclass Solution:def palindromePath(self, n: int, edges: list[list[int]], s: str, queries: list[str]) - list[bool]:# ---------- 1. 建图 ----------g [[] for _ in range(n)]for u, v in edges:g[u].append(v)g[v].append(u)# ---------- 2. DFS预处理深度、父节点、DFS序、初始前缀掩码 ----------LOG (n).bit_length()parent [[-1] * n for _ in range(LOG)]depth [0] * ntin [0] * ntout [0] * npref [0] * n # 从根到该节点的初始奇偶掩码timer 0ord_a ord(a)def dfs(u: int, fa: int):nonlocal timertimer 1tin[u] timerparent[0][u] fafor v in g[u]:if v fa:continuedepth[v] depth[u] 1pref[v] pref[u] ^ (1 (ord(s[v]) - ord_a))dfs(v, u)tout[u] timerpref[0] 1 (ord(s[0]) - ord_a)dfs(0, -1)# ---------- 3. 二进制提升表LCA ----------for k in range(1, LOG):for i in range(n):if parent[k-1][i] ! -1:parent[k][i] parent[k-1][parent[k-1][i]]def get_kth_ancestor(node: int, k: int) - int:for i in range(k.bit_length()):if (k i) 1:node parent[i][node]if node -1:return -1return nodedef lca(u: int, v: int) - int:if depth[u] depth[v]:u, v v, uv get_kth_ancestor(v, depth[v] - depth[u])if u v:return ufor k in range(LOG - 1, -1, -1):if parent[k][u] ! parent[k][v]:u parent[k][u]v parent[k][v]return parent[0][u]# ---------- 4. 树状数组区间异或更新 单点查询 ----------bit [0] * (n 2)def bit_add(idx: int, val: int):while idx n:bit[idx] ^ validx idx -idxdef range_xor(l: int, r: int, val: int):bit_add(l, val)bit_add(r 1, val)def point_query(idx: int) - int:res 0while idx 0:res ^ bit[idx]idx - idx -idxreturn res# ---------- 5. 处理查询 ----------chars [ord(c) - ord_a for c in s]ans []for q in queries:parts q.split()op parts[0]if op update:u int(parts[1])c ord(parts[2]) - ord_aif c ! chars[u]:diff (1 chars[u]) ^ (1 c) # 变化的位range_xor(tin[u], tout[u], diff)chars[u] celse: # queryu int(parts[1])v int(parts[2])w lca(u, v)# 当前前缀掩码 初始前缀 ^ BIT累积的更新cur_pref_u pref[u] ^ point_query(tin[u])cur_pref_v pref[v] ^ point_query(tin[v])mask cur_pref_u ^ cur_pref_v ^ (1 chars[w])# 判断是否只有0个或1个1即 mask 为0或2的幂ans.append((mask (mask - 1)) 0)return ans代码解释模块 说明DFS预处理 计算每个节点的depth、父节点parent[0]、DFS进入/退出时间tin/tout以及初始前缀掩码pref。同一子树在tin和tout之间形成连续区间。二进制提升LCA 构建parent[k][i]表示节点i的2^k级祖先lca函数用于O(log n)查询最近公共祖先。树状数组BIT 维护每个节点当前前缀掩码相对于初始值的增量。range_xor(tin[u], tout[u], diff)将u整棵子树所有节点的增量异或上diff。查询处理 当前前缀掩码 pref[u] ^ point_query(tin[u])。路径掩码公式为cur_pref_u ^ cur_pref_v ^ (1 chars[w])。用(mask (mask - 1)) 0判断是否可重排为回文串。更新处理 字符从old变为new时diff (1old) ^ (1new)表示变化的位对u的子树区间做异或更新即可。复杂度分析· 时间复杂度预处理O(n log n)每次查询/更新O(log n)· 空间复杂度O(n log n)LCA表 O(n)邻接表及其他数组