🧠
Brute Force (DFS with Cycle Detection and Path Tracking)
💡 Starting with brute force helps understand the problem's core challenges: detecting cycles and exploring all paths to find the longest valid one. Although inefficient, it lays the foundation for optimization.
Intuition
Try all paths starting from each node using DFS, track the path length only if adjacent characters differ, and detect cycles to avoid infinite loops.
Algorithm
- For each node, perform a DFS to explore all paths.
- Track visited nodes to detect cycles; if a cycle is found, return -1.
- For each path, only continue if the next node's character differs from the current node's character.
- Keep track of the maximum path length found.
💡 The challenge is managing cycle detection and character constraints simultaneously while exploring all paths, which can be overwhelming at first.
from collections import defaultdict
import sys
sys.setrecursionlimit(10**7)
def longest_path_brute_force(n, edges, s):
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
visited = [0] * n # 0=unvisited, 1=visiting, 2=visited
max_len = 0
def dfs(node, prev_char):
if visited[node] == 1:
# cycle detected
return -1
if visited[node] == 2:
# already processed
return 0
if s[node] == prev_char:
return 0
visited[node] = 1
max_path = 1
for nei in graph[node]:
res = dfs(nei, s[node])
if res == -1:
return -1
max_path = max(max_path, 1 + res)
visited[node] = 2
return max_path
for i in range(n):
res = dfs(i, '')
if res == -1:
return -1
max_len = max(max_len, res)
return max_len
# Example usage:
if __name__ == '__main__':
n = 5
edges = [[0,1],[1,2],[2,3],[3,4]]
s = "abaca"
print(longest_path_brute_force(n, edges, s)) # Output: 3
Line Notes
graph = defaultdict(list)Build adjacency list for efficient graph traversal
visited = [0] * nTrack node states to detect cycles and avoid reprocessing
if visited[node] == 1:Detect cycle if node is currently being visited
if s[node] == prev_char:Enforce character difference constraint between adjacent nodes
import java.util.*;
public class LongestPathBruteForce {
static Map<Integer, List<Integer>> graph = new HashMap<>();
static int[] visited; // 0=unvisited,1=visiting,2=visited
static String s;
static int n;
public static int dfs(int node, char prevChar) {
if (visited[node] == 1) return -1; // cycle
if (visited[node] == 2) return 0; // processed
if (s.charAt(node) == prevChar) return 0; // char constraint
visited[node] = 1;
int maxPath = 1;
for (int nei : graph.getOrDefault(node, Collections.emptyList())) {
int res = dfs(nei, s.charAt(node));
if (res == -1) return -1;
maxPath = Math.max(maxPath, 1 + res);
}
visited[node] = 2;
return maxPath;
}
public static int longestPath(int nodes, int[][] edges, String labels) {
n = nodes;
s = labels;
visited = new int[n];
graph.clear();
for (int[] edge : edges) {
graph.computeIfAbsent(edge[0], k -> new ArrayList<>()).add(edge[1]);
}
int maxLen = 0;
for (int i = 0; i < n; i++) {
int res = dfs(i, ' ');
if (res == -1) return -1;
maxLen = Math.max(maxLen, res);
}
return maxLen;
}
public static void main(String[] args) {
int n = 5;
int[][] edges = {{0,1},{1,2},{2,3},{3,4}};
String s = "abaca";
System.out.println(longestPath(n, edges, s)); // Output: 3
}
}
Line Notes
static Map<Integer, List<Integer>> graphAdjacency list for graph representation
static int[] visitedTrack node visitation state for cycle detection
if (visited[node] == 1) return -1;Detect cycle by revisiting a node in current path
if (s.charAt(node) == prevChar) return 0;Skip paths where adjacent chars are same
#include <bits/stdc++.h>
using namespace std;
unordered_map<int, vector<int>> graph;
vector<int> visited; // 0=unvisited,1=visiting,2=visited
string s;
int n;
int dfs(int node, char prevChar) {
if (visited[node] == 1) return -1; // cycle
if (visited[node] == 2) return 0; // processed
if (s[node] == prevChar) return 0; // char constraint
visited[node] = 1;
int maxPath = 1;
for (int nei : graph[node]) {
int res = dfs(nei, s[node]);
if (res == -1) return -1;
maxPath = max(maxPath, 1 + res);
}
visited[node] = 2;
return maxPath;
}
int longestPath(int nodes, vector<vector<int>>& edges, string labels) {
n = nodes;
s = labels;
visited.assign(n, 0);
graph.clear();
for (auto& e : edges) {
graph[e[0]].push_back(e[1]);
}
int maxLen = 0;
for (int i = 0; i < n; i++) {
int res = dfs(i, ' ');
if (res == -1) return -1;
maxLen = max(maxLen, res);
}
return maxLen;
}
int main() {
int n = 5;
vector<vector<int>> edges = {{0,1},{1,2},{2,3},{3,4}};
string s = "abaca";
cout << longestPath(n, edges, s) << "\n"; // Output: 3
return 0;
}
Line Notes
unordered_map<int, vector<int>> graph;Use hash map for adjacency list to handle sparse graphs
vector<int> visited;Track node states to detect cycles and avoid recomputation
if (visited[node] == 1) return -1;Cycle detection by revisiting a node in current recursion stack
if (s[node] == prevChar) return 0;Enforce adjacent character difference constraint
function longestPathBruteForce(n, edges, s) {
const graph = new Map();
for (const [u, v] of edges) {
if (!graph.has(u)) graph.set(u, []);
graph.get(u).push(v);
}
const visited = new Array(n).fill(0); // 0=unvisited,1=visiting,2=visited
let maxLen = 0;
function dfs(node, prevChar) {
if (visited[node] === 1) return -1; // cycle
if (visited[node] === 2) return 0; // processed
if (s[node] === prevChar) return 0; // char constraint
visited[node] = 1;
let maxPath = 1;
const neighbors = graph.get(node) || [];
for (const nei of neighbors) {
const res = dfs(nei, s[node]);
if (res === -1) return -1;
maxPath = Math.max(maxPath, 1 + res);
}
visited[node] = 2;
return maxPath;
}
for (let i = 0; i < n; i++) {
const res = dfs(i, '');
if (res === -1) return -1;
maxLen = Math.max(maxLen, res);
}
return maxLen;
}
// Example usage:
const n = 5;
const edges = [[0,1],[1,2],[2,3],[3,4]];
const s = "abaca";
console.log(longestPathBruteForce(n, edges, s)); // Output: 3
Line Notes
const graph = new Map();Build adjacency list using Map for efficient lookups
const visited = new Array(n).fill(0);Track node states to detect cycles and avoid repeated work
if (visited[node] === 1) return -1;Detect cycle by revisiting a node in current DFS path
if (s[node] === prevChar) return 0;Skip paths where adjacent characters are the same
TimeO(n * (n + m)) where m is number of edges
SpaceO(n + m) for graph and recursion stack
DFS from each node can explore many paths; worst case exponential due to repeated states without memoization
💡 For n=20, this approach may do millions of operations, making it impractical for large inputs.
Interview Verdict: TLE
This approach is too slow for large inputs but is essential to understand the problem and build towards efficient solutions.