千问 LeetCode 2791. 树中可以形成回文的路径数 Java实现
这是一道经典的树上路径计数问题结合了状态压缩和异或XOR的技巧。核心思路1. 回文判断条件一个字符串的字符重新排列后能形成回文当且仅当字符串中最多只有一个字符出现了奇数次其余字符均出现偶数次。2. 状态压缩与异或由于字符串只包含小写字母26个我们可以用一个 26 位的二进制数int 类型来表示当前路径上各字符出现次数的奇偶性。某一位为 1 表示该字符出现了奇数次为 0 表示出现了偶数次。在树上遍历时向下走一条边相当于对该边字符对应的二进制位进行异或操作。3. 路径转化树上任意两点 u 到 v 的路径可以看作是从 u 到根节点再从根节点到 v 的路径。其中从 u 和 v 的最近公共祖先LCA到根节点的这段路径被重复走了两次一去一回字符出现次数必然为偶数异或后为0不影响回文判断。因此我们只需要记录每个节点到根节点的路径状态。4. 计数逻辑使用 DFS 遍历树并用一个哈希表HashMap记录遍历过程中各种路径状态出现的次数。对于当前节点如果之前存在一个节点到根节点的状态与当前状态完全相同异或结果为0所有字符均为偶数次或仅相差一位异或结果只有一个1仅一个字符为奇数次那么这两个节点之间的路径就能形成回文。Java 代码实现import java.util.*;class Solution {public long countPalindromePaths(ListInteger parent, String s) {int n parent.size();// 1. 构建邻接表树结构ListInteger[] children new ArrayList[n];Arrays.setAll(children, e - new ArrayList());for (int i 1; i n; i) {int p parent.get(i);children[p].add(i);}// 2. 哈希表记录从根节点到当前路径的状态异或值出现的次数// 初始放入 0:1代表根节点之上的空路径状态MapInteger, Integer count new HashMap();count.put(0, 1);return dfs(0, 0, children, s.toCharArray(), count);}private long dfs(int node, int status, ListInteger[] children, char[] chars, MapInteger, Integer count) {long res 0;for (int child : children[node]) {// 计算从根节点到子节点 child 的路径状态// 异或上当前边上的字符对应的位int childStatus status ^ (1 (chars[child] - a));// 情况1之前有相同状态的路径所有字符出现偶数次异或后为0res count.getOrDefault(childStatus, 0);// 情况2之前有仅相差一个字符奇偶性的路径仅一个字符出现奇数次for (int i 0; i 26; i) {res count.getOrDefault(childStatus ^ (1 i), 0);}// 将当前子节点的状态加入哈希表供后续节点匹配count.put(childStatus, count.getOrDefault(childStatus, 0) 1);// 继续向下 DFSres dfs(child, childStatus, children, chars, count);}return res;}}复杂度分析* 时间复杂度O(n * A)其中 n 是节点数量A 是字符集大小本题为26。每个节点只会被遍历一次每次遍历需要枚举 26 个字母来寻找只差一位的状态。* 空间复杂度O(n)主要用于存储树的邻接表以及哈希表。在最坏情况下如链状树哈希表可能存储 O(n) 个不同的状态。