Open In App

Longest Path with Maximum Letter Frequency

Last Updated : 23 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given a graph with n vertex and m-directed edges. One lowercase letter is assigned to each vertex. The value of the path is the number of the most frequently occurring letters in the path. The task is to find a path whose value is the largest.

Example:

Input: n = 5, m = 4, node_value = “abaca”, edges[] = {1 2}, {1 3}, {3 4}, {4, 5}
Output: 3

Input: n = 6, m = 6, node_value = “xzyabc”, edges[] = {1, 2}, {3, 1}, {2, 3}, {5, 4}, {4, 3}, {6, 4}
Output: -1

Approach:

The idea is to use dynamic programming to avoid redundant traversal of nodes. Use a 2-D array ‘dp’, where dp[i][j] represents the maximum count of character ‘j’ when reaching node i. ‘i’ ranges from 1 to N, and ‘j’ from 0 to 25 (representing ‘a’ to ‘z’). Use topological sorting to calculate dp values, ensuring that nodes with edges to node ‘i’ are visited first. If a cycle is detected in the graph, the function returns -1 to indicate an infinitely large result due to repetitive traversal of the cycle.

Step-by-step approach:

  • Define ‘inDegree’ array for node indegrees, initially set to 0.
  • Iterate edges, updating ‘inDegree’ for each node.
  • Use a queue for topological order, enqueue nodes with ‘inDegree’ 0.
  • Pop the front element, increment character frequency in ‘dp’ array.
  • Update ‘dp’ array for adjacent nodes and adjust ‘inDegree’.
  • Enqueue nodes with ‘inDegree’ 0 after updating.
  • If queue has elements after ‘N’ pops, return -1 (cycle detected).
  • Return the maximum element in the ‘dp’ array.

Below is the implementation of the above approach:

C++




#include <bits/stdc++.h>
using namespace std;
 
int maxPathValue(int n, int m, vector<vector<int> >& edges,
                 string& values)
{
    // Adjacency list.
    vector<int> adj[n + 1];
 
    // To store the indegree of each node.
    vector<int> inDegree(n + 1, 0);
 
    // Iterate through the edges.
    for (vector<int> i : edges) {
        adj[i[0]].push_back(i[1]);
        inDegree[i[1]]++;
    }
 
    // Queue for traversing the nodes in the topological
    // order.
    queue<int> q;
 
    // To store the frequency of each character for each
    // node.
    vector<vector<int> > dp(n + 1, vector<int>(26, 0));
 
    // Push all nodes with 0 indegree into the queue.
    for (int i = 1; i <= n; i++) {
        if (inDegree[i] == 0) {
            q.push(i);
        }
    }
 
    // To count the number of nodes popped from the queue.
    int popCounts = 0;
    int ans = 0;
 
    while (q.empty() == false) {
        // Pop the front node.
        int curr = q.front();
        q.pop();
        popCounts++;
 
        // Increment the value of the character assigned to
        // the current node.
        dp[curr][values[curr - 1] - 'a']++;
        ans = max(ans, dp[curr][values[curr - 1] - 'a']);
 
        // Iterate through all adjacent nodes.
        for (int i : adj[curr]) {
            // Update the dp array for the adjacent node.
            for (int j = 0; j < 26; j++) {
                dp[i][j] = max(dp[i][j], dp[curr][j]);
            }
            inDegree[i]--;
 
            // Push the node into the queue if all incoming
            // edges to that node have been traversed.
            if (inDegree[i] == 0) {
                q.push(i);
            }
        }
        if (popCounts == n) {
            break;
        }
    }
    // Check whether there is a cycle or not.
    if (q.empty() == false || popCounts < n) {
        return -1;
    }
    return ans;
}
 
int main()
{
    // Example inputs
    int n = 5;
    int m = 4;
    vector<vector<int> > edges
        = { { 1, 2 }, { 1, 3 }, { 3, 4 }, { 4, 5 } };
    string values = "abaca";
 
    // Function call
    int result = maxPathValue(n, m, edges, values);
 
    // Display the result
    if (result != -1) {
        cout << "Maximum path value: " << result << endl;
    }
    else {
        cout << "-1" << endl;
    }
 
    return 0;
}


Java




import java.util.*;
 
public class MaxPathValue {
 
    // Function to find the maximum path value
    public static int
    maxPathValue(int n, int m, List<List<Integer> > edges,
                 String values)
    {
        // Adjacency list to represent the graph
        List<List<Integer> > adj = new ArrayList<>(n + 1);
        for (int i = 0; i <= n; i++) {
            adj.add(new ArrayList<>());
        }
 
        // To store the in-degree of each node
        List<Integer> inDegree = new ArrayList<>(
            Collections.nCopies(n + 1, 0));
 
        // Build the adjacency list and in-degree array
        for (List<Integer> edge : edges) {
            adj.get(edge.get(0)).add(edge.get(1));
            inDegree.set(edge.get(1),
                         inDegree.get(edge.get(1)) + 1);
        }
 
        // Queue for topological order traversal
        Queue<Integer> q = new LinkedList<>();
 
        // DP array to store the frequency of each character
        // for each node
        List<List<Integer> > dp = new ArrayList<>(n + 1);
        for (int i = 0; i <= n; i++) {
            dp.add(new ArrayList<>(
                Collections.nCopies(26, 0)));
        }
 
        // Push nodes with in-degree 0 into the queue
        for (int i = 1; i <= n; i++) {
            if (inDegree.get(i) == 0) {
                q.offer(i);
            }
        }
 
        int popCounts = 0;
        int ans = 0;
 
        // Perform topological order traversal
        while (!q.isEmpty()) {
            int curr = q.poll();
            popCounts++;
 
            // Increment the value of the character assigned
            // to the current node
            dp.get(curr).set(
                values.charAt(curr - 1) - 'a',
                dp.get(curr).get(values.charAt(curr - 1)
                                 - 'a')
                    + 1);
            ans = Math.max(
                ans, dp.get(curr).get(
                         values.charAt(curr - 1) - 'a'));
 
            // Update the DP array for adjacent nodes
            for (int i : adj.get(curr)) {
                for (int j = 0; j < 26; j++) {
                    dp.get(i).set(
                        j, Math.max(dp.get(i).get(j),
                                    dp.get(curr).get(j)));
                }
                inDegree.set(i, inDegree.get(i) - 1);
 
                // Push the node into the queue if all
                // incoming edges to that node have been
                // traversed
                if (inDegree.get(i) == 0) {
                    q.offer(i);
                }
            }
 
            // Break if all nodes have been processed
            if (popCounts == n) {
                break;
            }
        }
 
        // Check whether there is a cycle or not
        if (!q.isEmpty() || popCounts < n) {
            return -1;
        }
        return ans;
    }
 
    // Main method for testing
    public static void main(String[] args)
    {
        // Example inputs
        int n = 5;
        int m = 4;
        List<List<Integer> > edges = Arrays.asList(
            Arrays.asList(1, 2), Arrays.asList(1, 3),
            Arrays.asList(3, 4), Arrays.asList(4, 5));
        String values = "abaca";
 
        // Function call
        int result = maxPathValue(n, m, edges, values);
 
        // Display the result
        if (result != -1) {
            System.out.println("Maximum path value: "
                               + result);
        }
        else {
            System.out.println("-1");
        }
    }
}


Python3




# Python code for the above approach
 
from collections import deque
 
def maxPathValue(n, m, edges, values):
    # Adjacency list.
    adj = [[] for _ in range(n+1)]
    # vector<int> adj[n + 1];
 
    # To store the indegree of each node.
    inDegree = [0 for _ in range(n+1)]
 
    # Iterate through the edges.
    for i in edges:
        adj[i[0]].append(i[1])
        inDegree[i[1]] += 1
 
    # Queue for traversing the nodes in the
    # topological order.
    q = deque()
 
    # To store the frequency of each character
    # for each node.
    dp = [[0 for j in range(26)] for i in range(n+1)]
 
    # Push all nodes with 0 indegree into the queue.
    for i in range(1, n+1):
        if (inDegree[i] == 0):
            q.append(i)
 
    # To count the number of nodes popped from the queue.
    popCounts, ans = 0, 0
 
    while len(q):
        # Pop the front node.
        curr = q.popleft()
        popCounts += 1
 
        # Increment the value of the character assigned to
        # the current node.
        dp[curr][ord(values[curr - 1]) - ord('a')] += 1
        ans = max(ans, dp[curr][ord(values[curr - 1]) - ord('a')])
 
        # Iterate through all adjacent nodes.
        for i in adj[curr]:
            # Update the dp array for the adjacent node.
            for j in range(26):
                dp[i][j] = max(dp[i][j], dp[curr][j])
 
            inDegree[i] -= 1
 
            # Push the node into the queue if all incoming
            # edges to that node have been traversed.
            if (inDegree[i] == 0):
                q.append(i)
 
        if (popCounts == n):
            break
 
    # Check whether there is a cycle or not.
    if (len(q) > 0 or popCounts < n):
        return -1
    return ans
 
def main():
    # Example inputs
    n, m = 5, 4
 
    edges = [[1, 2], [1, 3], [3, 4], [4, 5]]
    values = "abaca"
 
    # Function call
    result = maxPathValue(n, m, edges, values)
 
    # Display the result
    if (result != -1):
        print("Maximum path value:", result)
    else:
        print("-1")
 
if __name__ == '__main__':
    main()
 
# This code is contributed by ragul21


C#




using System;
using System.Collections.Generic;
using System.Linq;
 
class MainClass {
    static int MaxPathValue(int n, int m, List<List<int>> edges, string values) {
        List<List<int>> adj = new List<List<int>>();
        for (int i = 0; i <= n; i++) {
            adj.Add(new List<int>());
        }
 
        List<int> inDegree = new List<int>(new int[n + 1]);
        List<List<int>> dp = new List<List<int>>();
        for (int i = 0; i <= n; i++) {
            dp.Add(Enumerable.Repeat(0, 26).ToList());
        }
 
        foreach (var edge in edges) {
            adj[edge[0]].Add(edge[1]);
            inDegree[edge[1]]++;
        }
 
        Queue<int> q = new Queue<int>();
 
        for (int i = 1; i <= n; i++) {
            if (inDegree[i] == 0) {
                q.Enqueue(i);
            }
        }
 
        int popCounts = 0;
        int ans = 0;
 
        while (q.Count != 0) {
            int curr = q.Dequeue();
            popCounts++;
 
            dp[curr][values[curr - 1] - 'a']++;
            ans = Math.Max(ans, dp[curr][values[curr - 1] - 'a']);
 
            foreach (int i in adj[curr]) {
                for (int j = 0; j < 26; j++) {
                    dp[i][j] = Math.Max(dp[i][j], dp[curr][j]);
                }
                inDegree[i]--;
 
                if (inDegree[i] == 0) {
                    q.Enqueue(i);
                }
            }
            if (popCounts == n) {
                break;
            }
        }
 
        if (q.Count != 0 || popCounts < n) {
            return -1;
        }
        return ans;
    }
 
    public static void Main (string[] args) {
        int n = 5;
        int m = 4;
        List<List<int>> edges = new List<List<int>> {
            new List<int> { 1, 2 },
            new List<int> { 1, 3 },
            new List<int> { 3, 4 },
            new List<int> { 4, 5 }
        };
        string values = "abaca";
 
        int result = MaxPathValue(n, m, edges, values);
 
        if (result != -1) {
            Console.WriteLine("Maximum path value: " + result);
        } else {
            Console.WriteLine("-1");
        }
    }
}


Javascript




function maxPathValue(n, m, edges, values) {
    const adj = new Array(n + 1).fill(null).map(() => []);
    const inDegree = new Array(n + 1).fill(0);
    const dp = new Array(n + 1).fill(null).map(() => new Array(26).fill(0));
 
    for (const [u, v] of edges) {
        adj[u].push(v);
        inDegree[v]++;
    }
 
    const q = [];
    let popCounts = 0;
    let ans = 0;
 
    for (let i = 1; i <= n; i++) {
        if (inDegree[i] === 0) {
            q.push(i);
        }
    }
 
    while (q.length > 0) {
        const curr = q.shift();
        popCounts++;
 
        dp[curr][values.charCodeAt(curr - 1) - 'a'.charCodeAt(0)]++;
        ans = Math.max(ans, dp[curr][values.charCodeAt(curr - 1) - 'a'.charCodeAt(0)]);
 
        for (const next of adj[curr]) {
            for (let j = 0; j < 26; j++) {
                dp[next][j] = Math.max(dp[next][j], dp[curr][j]);
            }
 
            inDegree[next]--;
 
            if (inDegree[next] === 0) {
                q.push(next);
            }
        }
 
        if (popCounts === n) {
            break;
        }
    }
 
    if (q.length > 0 || popCounts < n) {
        return -1;
    }
 
    return ans;
}
 
// Example inputs
const n = 5;
const m = 4;
const edges = [[1, 2], [1, 3], [3, 4], [4, 5]];
const values = 'abaca';
 
// Function call
const result = maxPathValue(n, m, edges, values);
 
// Display the result
if (result !== -1) {
    console.log(`Maximum path value: ${result}`);
} else {
    console.log('-1');
}
 
 
// This code is contributed by akshitaguprzj3


Output

Maximum path value: 3









Time Complexity: O(N), Where ‘N’ is the number of nodes in the given graph.
Auxiliary Space: O(N)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads