Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1857. Largest Color Value in a Directed Graph - Leetcode Solution

Code Implementation

from collections import deque, defaultdict

class Solution:
    def largestPathValue(self, colors: str, edges: list[list[int]]) -> int:
        n = len(colors)
        graph = [[] for _ in range(n)]
        indegree = [0] * n
        for u, v in edges:
            graph[u].append(v)
            indegree[v] += 1

        # color_count[node][color] = max count of color along any path ending at node
        color_count = [ [0]*26 for _ in range(n) ]
        queue = deque()

        for i in range(n):
            if indegree[i] == 0:
                queue.append(i)
        for i in range(n):
            color_count[i][ord(colors[i])-ord('a')] = 1

        visited = 0
        answer = 0
        while queue:
            node = queue.popleft()
            visited += 1
            for c in range(26):
                answer = max(answer, color_count[node][c])
            for nei in graph[node]:
                for c in range(26):
                    color_count[nei][c] = max(color_count[nei][c], color_count[node][c] + (1 if c == ord(colors[nei])-ord('a') else 0))
                indegree[nei] -= 1
                if indegree[nei] == 0:
                    queue.append(nei)
        return answer if visited == n else -1
      
#include <vector>
#include <queue>
#include <string>
#include <algorithm>
using namespace std;

class Solution {
public:
    int largestPathValue(string colors, vector<vector<int>>& edges) {
        int n = colors.size();
        vector<vector<int>> graph(n);
        vector<int> indegree(n, 0);
        for (auto& e : edges) {
            graph[e[0]].push_back(e[1]);
            indegree[e[1]]++;
        }
        vector<vector<int>> color_count(n, vector<int>(26, 0));
        queue<int> q;
        for (int i = 0; i < n; ++i) {
            if (indegree[i] == 0) q.push(i);
            color_count[i][colors[i] - 'a'] = 1;
        }
        int visited = 0, answer = 0;
        while (!q.empty()) {
            int node = q.front(); q.pop();
            visited++;
            for (int c = 0; c < 26; ++c)
                answer = max(answer, color_count[node][c]);
            for (int nei : graph[node]) {
                for (int c = 0; c < 26; ++c) {
                    int add = (c == colors[nei] - 'a') ? 1 : 0;
                    color_count[nei][c] = max(color_count[nei][c], color_count[node][c] + add);
                }
                indegree[nei]--;
                if (indegree[nei] == 0)
                    q.push(nei);
            }
        }
        return visited == n ? answer : -1;
    }
};
      
import java.util.*;

class Solution {
    public int largestPathValue(String colors, int[][] edges) {
        int n = colors.length();
        List<List<Integer>> graph = new ArrayList<>();
        int[] indegree = new int[n];
        for (int i = 0; i < n; ++i) graph.add(new ArrayList<>());
        for (int[] e : edges) {
            graph.get(e[0]).add(e[1]);
            indegree[e[1]]++;
        }
        int[][] colorCount = new int[n][26];
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; ++i) {
            if (indegree[i] == 0) queue.add(i);
            colorCount[i][colors.charAt(i) - 'a'] = 1;
        }
        int visited = 0, answer = 0;
        while (!queue.isEmpty()) {
            int node = queue.poll();
            visited++;
            for (int c = 0; c < 26; ++c)
                answer = Math.max(answer, colorCount[node][c]);
            for (int nei : graph.get(node)) {
                for (int c = 0; c < 26; ++c) {
                    int add = (c == colors.charAt(nei) - 'a') ? 1 : 0;
                    colorCount[nei][c] = Math.max(colorCount[nei][c], colorCount[node][c] + add);
                }
                indegree[nei]--;
                if (indegree[nei] == 0)
                    queue.add(nei);
            }
        }
        return visited == n ? answer : -1;
    }
}
      
/**
 * @param {string} colors
 * @param {number[][]} edges
 * @return {number}
 */
var largestPathValue = function(colors, edges) {
    const n = colors.length;
    const graph = Array.from({length: n}, () => []);
    const indegree = Array(n).fill(0);
    for (const [u, v] of edges) {
        graph[u].push(v);
        indegree[v]++;
    }
    const colorCount = Array.from({length: n}, () => Array(26).fill(0));
    const queue = [];
    for (let i = 0; i < n; ++i) {
        if (indegree[i] === 0) queue.push(i);
        colorCount[i][colors.charCodeAt(i) - 97] = 1;
    }
    let visited = 0, answer = 0;
    while (queue.length) {
        const node = queue.shift();
        visited++;
        for (let c = 0; c < 26; ++c)
            answer = Math.max(answer, colorCount[node][c]);
        for (const nei of graph[node]) {
            for (let c = 0; c < 26; ++c) {
                colorCount[nei][c] = Math.max(colorCount[nei][c], colorCount[node][c] + (c === colors.charCodeAt(nei) - 97 ? 1 : 0));
            }
            indegree[nei]--;
            if (indegree[nei] === 0)
                queue.push(nei);
        }
    }
    return visited === n ? answer : -1;
};
      

Problem Description

You are given a directed graph with n nodes, labeled from 0 to n - 1. Each node is assigned a color, represented by a lowercase English letter in the string colors (where colors[i] is the color of node i). The graph's edges are given as a list edges, where each edge is a pair [u, v] indicating a directed edge from node u to node v.

Your task is to find the largest number of nodes with the same color that can be found on any path in the graph. If the graph contains a cycle, return -1 (since a path could be infinitely long).

Key constraints:

  • Each node has exactly one color.
  • There may be multiple edges and cycles in the graph.
  • You must not count the same node more than once in a path.
  • If there is a cycle in the graph, return -1.

Thought Process

The core of this problem is to find, for every path in the directed graph, the maximum count of any single color that appears on that path. However, the presence of cycles complicates things, since cycles could allow us to revisit nodes indefinitely and artificially inflate counts.

A brute-force approach might be to enumerate all possible paths and count the colors along each one, but this is computationally infeasible due to the exponential number of paths in a graph, especially with cycles.

Instead, we notice that if we process the graph in topological order (i.e., from sources to sinks, only visiting nodes after all their dependencies), we can efficiently propagate color counts along all possible paths. This way, for each node, we can keep track of the maximum count of each color that can be achieved along any path ending at that node.

Detecting cycles is also crucial. If the graph contains a cycle, it's impossible to determine a finite maximum, so we must return -1 in that case.

Solution Approach

The solution uses a combination of topological sorting and dynamic programming to efficiently compute the answer.

  • Step 1: Build the Graph and Compute Indegrees
    Construct an adjacency list for the graph and an array to keep track of the indegree (number of incoming edges) for each node.
  • Step 2: Initialize Color Counts
    For each node, create an array of size 26 (for each lowercase letter) to store the maximum count of each color along any path ending at that node. Initialize the color count for the node's own color to 1.
  • Step 3: Topological Sort (Kahn's Algorithm)
    Use a queue to process nodes with indegree zero. For each node, update its neighbors' color counts by considering the current node's color counts, and increment the count if the neighbor's color matches the current color being considered.
  • Step 4: Propagate Color Counts
    For each neighbor, for each color, set the neighbor's color count to the maximum of its current value and the current node's color count (plus one if the neighbor's color matches).
  • Step 5: Detect Cycles
    Keep track of the number of nodes processed. If this number is less than the total number of nodes, a cycle exists, and we return -1.
  • Step 6: Find the Answer
    The answer is the maximum value among all color counts for all nodes.

This approach ensures each node and edge is processed only once, and color counts are updated efficiently using dynamic programming.

Example Walkthrough

Example Input:
colors = "abaca"
edges = [[0,1],[0,2],[2,3],[3,4]]

  1. Build the graph:
    • 0 → 1, 0 → 2, 2 → 3, 3 → 4
  2. Initialize color counts:
    • Node 0: 'a' → 1
    • Node 1: 'b' → 1
    • Node 2: 'a' → 1
    • Node 3: 'c' → 1
    • Node 4: 'a' → 1
  3. Process nodes in topological order:
    • Start with node 0 (indegree 0). Update color counts for nodes 1 and 2.
    • Node 2 now has 'a' → 2 because path 0→2 both have 'a'.
    • Process node 1 (no outgoing edges).
    • Process node 2, update node 3: node 3's 'a' count is now 2 (from node 2's 'a' count).
    • Process node 3, update node 4: node 4's 'a' count is now 3 (from node 3's 'a' count plus node 4's own 'a').
  4. Find the maximum color count:
    • Node 4 has 'a' count of 3, which is the largest color value along any path.
  5. No cycles detected:
    • All nodes processed, so return 3.

Time and Space Complexity

  • Brute-force approach:
    • Enumerating all paths is exponential in the number of nodes and edges (worst-case O(2^n)), which is infeasible for large graphs.
  • Optimized approach:
    • Time Complexity: O(n + m + 26n), where n is the number of nodes and m is the number of edges. Each node and edge is processed once, and for each node, we update 26 color counts.
    • Space Complexity: O(n * 26) for the color count arrays, plus O(n + m) for the graph representation and indegree array.

Summary

This problem demonstrates how to combine topological sorting and dynamic programming to efficiently compute path statistics in a directed graph. By carefully propagating color counts and detecting cycles, we avoid the pitfalls of brute-force enumeration and ensure correctness even in complex graphs. The use of a color count table at each node is the key insight that makes the solution both elegant and efficient.