Compute Effective Letter Permissions in a DAG
SnowflakeSoftware Engineer · Technical ScreenLast updated
Compute the effective lowercase letter permissions for every node in a directed acyclic graph (DAG), where granted permissions propagate transitively to all descendants while explicit denials apply only locally.
Problem Statement
In an access control system represented by a Directed Acyclic Graph (DAG), nodes represent security principals or resources, and directed edges (u, v) indicate that v is a descendant of u (i.e., v inherits access privileges from u).
Each node may define:
- Explicit allows: A set of lowercase English letters representing permissions granted at this node.
- Explicit disallows: A set of lowercase English letters representing permissions denied locally at this node.
Inheritance and Permission Rules
- Propagation of Allows: A permission letter
cis available to nodevifcis explicitly allowed byvitself or by any ofv's ancestors. - Local Disallows: An explicit disallow on a node
vremoves that letter fromv's effective permissions. Disallow settings are strictly local—they do not propagate to descendants and do not prevent descendants from inheriting the permission from ancestors. - Effective Permissions: For any node
v, its effective permissions are the set of letters available tov(via inheritance or its own explicit allows) minusv's explicit disallows.
Write a function to return the effective permissions for all nodes. The result must be a list of (node, permissions) pairs sorted lexicographically by node, with each permissions string sorted alphabetically.
Function Signature
def compute_effective_permissions(
nodes: list[str],
edges: list[tuple[str, str]],
allows: dict[str, str],
disallows: dict[str, str]
) -> list[tuple[str, str]]:
pass
Input and Output Format
- Input:
nodes: A list of unique strings representing node identifiers.edges: A list of pairs(u, v)representing a directed edge from parentuto childv.allows: A dictionary mapping node IDs to strings of unique lowercase letters ('a'-'z'). If a node is missing from the dictionary, it has no explicit allows.disallows: A dictionary mapping node IDs to strings of unique lowercase letters ('a'-'z'). If a node is missing, it has no explicit disallows.
- Output:
- A list of tuples
(node_id, effective_permissions)whereeffective_permissionsis a string of sorted unique lowercase letters. - The list must be sorted in ascending lexicographical order by
node_id.
- A list of tuples
Constraints
1 <= len(nodes) <= 10^40 <= len(edges) <= 2 * 10^4- Node IDs are non-empty alphanumeric strings.
- The given graph is guaranteed to be a Directed Acyclic Graph (DAG).
- All permission characters are lowercase English letters (
'a'to'z').
Examples
Example 1
nodes = ["A", "B", "C"]
edges = [("A", "B"), ("B", "C")]
allows = {"A": "rw", "B": "x"}
disallows = {"B": "w"}
# Output:
# [("A", "rw"), ("B", "rx"), ("C", "rwx")]
Explanation:
A: Inherits nothing. Allows'r','w'. No disallow. Effective:"rw".B: Inherits'r','w'fromA; explicitly allows'x'. Disallows'w'. Effective:{'r', 'w', 'x'} - {'w'} = {'r', 'x'}->"rx".C: Inherits'r','w'from ancestorA, and'x'from ancestorB.B's disallow of'w'is local and does not affectC. Effective:"rwx".
Example 2
nodes = ["U", "V", "W"]
edges = [("U", "W"), ("V", "W")]
allows = {"U": "a", "V": "b", "W": "c"}
disallows = {"W": "b"}
# Output:
# [("U", "a"), ("V", "b"), ("W", "ac")]
Explanation:
Whas two parents:UandV. It inherits'a'and'b', and allows'c'. SinceWlocally disallows'b', its effective set is{'a', 'c'}->"ac".
Hints
Hint 1
Since the permissions are restricted to 26 lowercase English letters, consider using a 26-bit integer bitmask to represent permission sets in O(1) time.
Hint 2
Perform a topological sort to process nodes in ancestor-to-descendant order so that when processing node v, all its parents' accumulated allowed permissions are already known.
Hint 3
Remember that disallows apply only to the current node: propagate accumulated allows (not effective permissions) from parent to child.
Follow-up questions
- How would your approach change if permissions were arbitrary string tokens instead of single characters from a 26-letter alphabet?
- Suppose disallows also propagated to all descendants and took precedence over allows (hierarchical deny). How would you modify the propagation logic?
- If the graph were dynamic with frequent permission queries and edge insertions, what data structures would you use to support low-latency reads?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.