We are given a rooted tree where each node has a label (a character). We need to find the longest path in this tree such that no two adjacent nodes along the path share the same character. The answer is the number of nodes in this path, not the number of edges.
The tree is given as a parent array, where parent[i] is the parent of node i, and node 0 is the root (its parent is -1). The first step is building an adjacency list from this array.
The path can go through any part of the tree. It does not need to start or end at the root, and it does not need to go strictly downward. A path can travel upward through a node and back down to another child, forming an inverted-V shape. But a path cannot branch, so at any given node, the path can extend through at most two of its children.
This is structurally similar to the "diameter of a tree" problem. At each node, we compute the two longest downward chains (extending through children with different characters), combine them through the current node to get a candidate path, and report the single best chain upward for the parent to use.
1 <= n <= 10^5 -- With n up to 100,000, an O(n^2) approach performs on the order of 10^10 operations and will time out. We need O(n).parent represents a valid tree -- No cycles, connected. We can build an adjacency list and do a single DFS or BFS traversal.parent[0] == -1 -- Node 0 is always the root, so we can root the traversal there.For every node in the tree, start a DFS and explore all paths originating from that node, tracking the longest one where no two adjacent nodes share the same character.
From any starting node, we explore all neighbors. For each neighbor that has a different character, we extend the path and continue recursively. We avoid revisiting the node we came from; since this is a tree with no cycles, that single check is enough to prevent loops.
For each starting node, the DFS visits up to O(n) nodes, and we repeat this for all n nodes, giving O(n^2) total work. This recomputes the same sub-chains from scratch on every restart, which is the source of the inefficiency.
i from 0 to n-1, run a DFS that explores all valid paths starting from i.This is too slow for n up to 10^5. The next approach computes the longest valid chain from each node exactly once, bottom-up, bringing the cost down to O(n).
This is the tree-diameter technique adapted for the character constraint. Instead of launching a DFS from every node, we do a single post-order DFS from the root. At each node, we compute the longest chain of nodes (with adjacent characters differing) extending downward through any of its children, and we return that chain length to the parent so it can build on it.
A path passing through a node can extend into at most two of its children. So the longest path through a node equals 1 (for itself) plus the two longest valid child chains. When we report upward to the parent, we can extend in only one direction, so we return 1 plus the single longest child chain.
A child chain counts as valid only when the child has a different character from the current node. If child and parent share the same character, the edge between them cannot be part of any valid path, so that child contributes 0 to the current node's chains.
Every path in a tree passes through exactly one node closest to the root, call it the path's apex. At its apex, the path descends in at most two directions, through two distinct children. Computing 1 + longest + secondLongest at every node during a single post-order pass therefore evaluates each candidate path exactly once, at its apex, so the global maximum is the true answer.
s[child] != s[node], the chain from that child is valid. Track the top two.1 + longest + secondLongest. Update the global maximum.1 + longest to the parent (the best single chain extending downward from this node).On a deeply skewed tree, the recursion depth reaches O(n), which can overflow the call stack. The next approach keeps the same O(n) time but processes nodes bottom-up with an explicit queue instead of recursion.
Instead of DFS recursion, we process the tree bottom-up with a topological sort. We start from the leaf nodes (which have no children) and work up toward the root.
The computation mirrors Approach 2, but the traversal uses BFS from the leaves. Each leaf has a chain length of 1. Once all children of a node have been processed, we combine the two longest valid child chains (those where the child character differs from the parent), update the global max, and propagate the single best chain upward.
We track how many children each node still has unprocessed. When that count reaches zero, the node is ready to process, and its chain value propagates to its parent. This is Kahn's algorithm applied to a tree.
A node is enqueued only when its unprocessed-child count reaches zero, so every node is processed after all of its children. By that point each child's chain length is finalized and cannot change. This gives the same processing order as the post-order DFS in Approach 2, so the per-node computation is identical. Only the scheduling differs: an explicit queue replaces the recursion stack, which removes the risk of stack overflow on a skewed tree.