Before attempting this problem, you should be comfortable with:
The problem asks for the maximum count of any single color along any valid path in the graph. A direct approach is to try every possible starting node and every possible color, then use DFS to explore all paths and count how many nodes of that color we encounter. If we detect a cycle during DFS, the answer is -1 since valid paths cannot contain cycles.
0 to n-1, and for each color from 0 to 25:-1 immediately.class Solution:
def largestPathValue(self, colors: str, edges: list[list[int]]) -> int:
n = len(colors)
adj = [[] for _ in range(n)]
for u, v in edges:
adj[u].append(v)
visit = [False] * n
def dfs(node, c):
if visit[node]:
return float("inf")
visit[node] = True
clrCnt = 0
for nei in adj[node]:
cur = dfs(nei, c)
if cur == float("inf"):
return cur
clrCnt = max(clrCnt, cur)
visit[node] = False
return clrCnt + (c == (ord(colors[node]) - ord('a')))
res = -1
for i in range(n):
for c in range(26):
cnt = dfs(i, c)
if cnt == float("inf"):
return -1
res = max(res, cnt)
return resWhere is the number of verticies and is the number of edges.
Instead of checking one color at a time, we can track all 26 color counts simultaneously for each node. Using memoization, once we compute the maximum color counts reachable from a node, we store and reuse that result. We use two sets: one for globally visited nodes (already fully processed) and one for the current DFS path (to detect cycles).
count[node][color] to store the maximum count of each color reachable from each node.0.1.count[node][color], or -1 if a cycle was detected.class Solution:
def largestPathValue(self, colors: str, edges: list[list[int]]) -> int:
adj = defaultdict(list)
for src, dst in edges:
adj[src].append(dst)
def dfs(node):
if node in path:
return float("inf")
if node in visit:
return 0
visit.add(node)
path.add(node)
colorIndex = ord(colors[node]) - ord('a')
count[node][colorIndex] = 1
for nei in adj[node]:
if dfs(nei) == float("inf"):
return float("inf")
for c in range(26):
count[node][c] = max(
count[node][c],
(1 if c == colorIndex else 0) + count[nei][c]
)
path.remove(node)
return 0
n, res = len(colors), 0
visit, path = set(), set()
count = [[0] * 26 for _ in range(n)]
for i in range(n):
if dfs(i) == float("inf"):
return -1
res = max(res, max(count[i]))
return resWhere is the number of verticies and is the number of edges.
Since we need valid paths in a directed graph, topological sorting naturally fits. We process nodes in topological order using Kahn's algorithm (BFS with indegree tracking). For each node, we propagate color counts to its neighbors before they are processed. If we cannot process all nodes, a cycle exists. This approach avoids recursion and handles cycle detection elegantly.
count[node][color] for tracking color frequencies.0 to a queue.0, add it to the queue.n, return the result. Otherwise, return -1 (cycle detected).class Solution:
def largestPathValue(self, colors: str, edges: list[list[int]]) -> int:
n = len(colors)
adj = [[] for _ in range(n)]
indegree = [0] * n
count = [[0] * 26 for _ in range(n)]
for u, v in edges:
adj[u].append(v)
indegree[v] += 1
q = deque()
for i in range(n):
if indegree[i] == 0:
q.append(i)
visit = res = 0
while q:
node = q.popleft()
visit += 1
colorIndex = ord(colors[node]) - ord('a')
count[node][colorIndex] += 1
res = max(res, count[node][colorIndex])
for nei in adj[node]:
for c in range(26):
count[nei][c] = max(count[nei][c], count[node][c])
indegree[nei] -= 1
if indegree[nei] == 0:
q.append(nei)
return res if visit == n else -1Where is the number of verticies and is the number of edges.
The problem requires returning -1 if the graph contains a cycle, since cycles mean there are infinitely long paths. A common mistake is forgetting to track the current DFS path separately from globally visited nodes. Using only a single visited set will not distinguish between nodes that are ancestors in the current path (indicating a cycle) versus nodes that were visited in a completely different traversal.
When using DFS with memoization, you need two separate tracking mechanisms: one for nodes currently in the recursion stack (to detect cycles) and one for nodes that have been fully processed (to avoid recomputation). Mixing these up leads to either false cycle detection or infinite loops.
When computing the maximum color count for a node, you must consider the counts from all reachable nodes, not just immediate neighbors. The DP state count[node][color] should represent the maximum count of that color on any path starting from that node. Forgetting to take the maximum across all neighbors or failing to add the current node's color contribution leads to incorrect results.
In the topological sort approach, a node should only be processed after all its incoming edges have been handled. If you process a node too early (before all predecessors have propagated their color counts), you'll miss potential maximum values from paths that haven't been fully computed yet.
Colors are given as lowercase letters but need to be converted to indices 0-25 for array access. A common bug is incorrectly converting characters (e.g., forgetting to subtract 'a') or mixing up when to add the current node's color contribution (should be added exactly once per node on the path).