You are given a tree (i.e. a connected, undirected graph that has no cycles) rooted at node 0
consisting of n
nodes numbered from 0
to n - 1
. The tree is represented by a 0-indexed array parent
of size n
, where parent[i]
is the parent of node i
. Since node 0
is the root, parent[0] == -1
.
You are also given a string s
of length n
, where s[i]
is the character assigned to node i
.
Return the length of the longest path in the tree such that no pair of adjacent nodes on the path have the same character assigned to them.
Example 1:
Input: parent = [-1,0,0,1,1,2], s = "abacbe" Output: 3 Explanation: The longest path where each two adjacent nodes have different characters in the tree is the path: 0 -> 1 -> 3. The length of this path is 3, so 3 is returned. It can be proven that there is no longer path that satisfies the conditions.
Example 2:
Input: parent = [-1,0,0,0], s = "aabc" Output: 3 Explanation: The longest path where each two adjacent nodes have different characters is the path: 2 -> 0 -> 3. The length of this path is 3, so 3 is returned.
Constraints:
n == parent.length == s.length
1 <= n <= 105
0 <= parent[i] <= n - 1
for all i >= 1
parent[0] == -1
parent
represents a valid tree.s
consists of only lowercase English letters.This problem asks us to find the longest path in a tree where no two adjacent nodes have the same character. We can solve this efficiently using Depth-First Search (DFS) with a dynamic programming approach.
Approach:
Tree Representation: The input parent
array represents a tree structure. We first convert this into an adjacency list representation for easier traversal. The adjacency list g
stores for each node, a list of its children.
Depth-First Search (DFS): We perform a DFS traversal starting from the root node (node 0). For each node i
, we recursively explore its children.
Dynamic Programming: For each child j
of node i
, we check if the characters at nodes i
and j
are different (s[i] != s[j]
). If they are different, it means we can extend the path.
dfs(j)
recursively computes the length of the longest path starting from child j
that satisfies the condition.dfs(j)
to account for the edge between i
and j
. This gives us the length x
of a path starting at node i
and going through j
.mx
: tracks the maximum length of a path extending from the current node i
through any of its children.ans
: tracks the maximum length of any path found so far in the entire tree.Updating the Result: When we find a path from i
through j
with different adjacent characters, we update ans
using the formula ans = max(ans, mx + x)
. This captures the case where we might have two long paths extending from different children of the current node.
Return Value: The function dfs
returns mx
, the maximum length of a path from the current node. The main function then returns ans + 1
because ans
represents the length of the longest path excluding the root node; we add 1 to include the root.
Time and Space Complexity:
g
and the recursion stack during the DFS. In the worst case (a skewed tree), the recursion depth could be O(N).Code (Python):
from collections import defaultdict
class Solution:
def longestPath(self, parent: List[int], s: str) -> int:
g = defaultdict(list)
for i in range(1, len(parent)):
g[parent[i]].append(i)
ans = 0
def dfs(i: int) -> int:
nonlocal ans
mx = 0
for j in g[i]:
x = dfs(j) + 1
if s[i] != s[j]:
ans = max(ans, mx + x)
mx = max(mx, x)
return mx
dfs(0)
return ans + 1
The code in other languages (Java, C++, Go, TypeScript) follows the same logic, differing only in syntax and data structure implementations. They all maintain the same O(N) time and space complexity.