Talentd
FleetCode
Make and follow sheets.Sign in

Pyramid Transition Matrix solution and explanation

MediumBit ManipulationBreadth-First SearchDepth-First Search6 min read60.6% acceptanceUpdated 9 Apr 2026
Pyramid Transition Matrix | Detailed Intuition | Beginner Friendly | Dry Runs | Leetcode 756 | MIKcodestorywithMIK · 40:45 · 8K viewsWatch on YouTube
9 more videos on this problem
  1. Pyramid Transition Matrix | Detailed Intuition | Beginner Friendly | Dry Runs | Leetcode 756 | MIKcodestorywithMIK · 40:45 · 8K views
  2. 756. Pyramid Transition Matrix (Leetcode Medium)Programming Live with Larry · 42:19 · 1K views
  3. 756. Pyramid Transition Matrix | Leetcode Daily - PythonLeetcode Daily · 9:05 · 1K views
  4. Pyramid Transition Matrix | LeetCode 756 | Backtracking & Row-by-Row MemoizationSanyam IIT Guwahati · 29:52 · 1K views
  5. Pyramid Transition Matrix 🔥 | LeetCode 756 | Backtracking + HashMap | Optimal Explanation Study Placement · 27:43 · 1K views
  6. Daily Leetcode #2099: Dec 29, 2025 - 756. Pyramid Transition MatrixProgramming Live with Larry · 13:53 · 760 views
  7. Pyramid Transition Matrix | Recursion | DP | Leetcode 756Techdose · 41:08 · 528 views
  8. Pyramid Transition Matrix | LeetCode 756 - Pythonimpoldev · 9:35 · 458 views
  9. 756. Pyramid Transition Matrix | December Daily Challenge | C++Priyanshi Porwal · 30:53 · 283 views
  10. Pyramid Transition Matrix | Leetcode 756 | Java HindiKernel Queen 👑 · 13:22 · 191 views

The problem

You are stacking blocks to form a pyramid. Each block has a color, which is represented by a single letter. Each row of blocks contains one less block than the row beneath it and is centered on top.

To make the pyramid aesthetically pleasing, there are only specific triangular patterns that are allowed. A triangular pattern consists of a single block stacked on top of two blocks. The patterns are given as a list of three-letter strings allowed, where the first two characters of a pattern represent the left and right bottom blocks respectively, and the third character is the top block.

  • For example, "ABC" represents a triangular pattern with a 'C' block stacked on top of an 'A' (left) and 'B' (right) block. Note that this is different from "BAC" where 'B' is on the left bottom and 'A' is on the right bottom.

You start with a bottom row of blocks bottom, given as a single string, that you must use as the base of the pyramid.

Given bottom and allowed, return true if you can build the pyramid all the way to the top such that every triangular pattern in the pyramid is in allowed, or false otherwise.

Example 1:

Input: bottom = "BCD", allowed = ["BCC","CDE","CEA","FFF"]
Output: true
Explanation: The allowed triangular patterns are shown on the right.
Starting from the bottom (level 3), we can build "CE" on level 2 and then build "A" on level 1.
There are three triangular patterns in the pyramid, which are "BCC", "CDE", and "CEA". All are allowed.

Example 2:

Input: bottom = "AAAA", allowed = ["AAB","AAC","BCD","BBE","DEF"]
Output: false
Explanation: The allowed triangular patterns are shown on the right.
Starting from the bottom (level 4), there are multiple ways to build level 3, but trying all the possibilites, you will get always stuck before building level 1.

Constraints:

  • 2 <= bottom.length <= 6
  • 0 <= allowed.length <= 216
  • allowed[i].length == 3
  • The letters in all input strings are from the set {'A', 'B', 'C', 'D', 'E', 'F'}.
  • All the values of allowed are unique.

Approach overview

Problem Overview: You are given a bottom row of blocks and a list of allowed triples ABC, meaning if blocks A and B are adjacent, block C can be placed above them. The task is to determine whether you can build the pyramid all the way to a single block at the top.

Approach 1: Dynamic Programming / DFS State Building (Time: O(2^n), Space: O(2^n))

Preprocess the allowed triples into a mapping where each pair (A, B) points to all possible top blocks. Starting from the bottom string, recursively construct the next level by checking each adjacent pair and generating all valid characters that can sit above it. This naturally forms a search tree where each level shortens the row by one. Memoization (DP on strings) avoids recomputing states that were already proven impossible, which drastically prunes the search space. The recursion is essentially a depth-first search over possible rows while caching failed configurations.

Approach 2: Greedy Expansion with Bitmask Optimization (Time: O(2^n), Space: O(26^2))

Instead of storing lists of characters for each pair, encode allowed transitions using bitmasks. Each pair (A,B) maps to a 26-bit mask representing all valid top blocks. While generating the next row, use bit operations to quickly enumerate candidates. This approach aggressively prunes invalid branches early and reduces memory overhead compared to storing lists. It still explores combinations using recursion or stack-based search similar to breadth-first search, but bit manipulation speeds up candidate checks.

Recommended for interviews: The DFS with memoization approach is the most common solution. It clearly models the pyramid-building process and demonstrates understanding of recursion, state pruning, and caching. Adding a bitmask optimization shows stronger mastery of bit manipulation and performance tuning. Interviewers mainly look for the recursive state generation plus memoization.

Approach 1: Dynamic Programming Approach

This approach involves using dynamic programming to break the problem into smaller overlapping subproblems. Our goal is to use past solutions to solve larger problems more efficiently.

def dynamic_programming_example():
    # Example dynamic programming logic
    pass # Replace with algorithm logic

dynamic_programming_example()
public class DynamicProgrammingExample {
    public static void main(String[] args) {
        dynamicProgrammingExample();
    }

    public static void dynamicProgrammingExample() {
        // Example dynamic programming logic
    }
}
#include <iostream>
#include <vector>

void dynamicProgrammingExample() {
    // Example dynamic programming logic
}

int main() {
    dynamicProgrammingExample();
    return 0;
}

In Python, the dynamic programming approach involves defining a base case and using a list to store intermediate results. This helps to avoid repeated calculations.

Complexity

Time Complexity: O(n^2)
Space Complexity: O(n)

Try this approach in the editor

Approach 2: Greedy Algorithm

The greedy approach makes locally optimal choices at each step with the hope of finding a global optimum. This can be effective in problems where a correct local solution also leads to a correct global solution.

function greedyAlgorithmExample() {
    // Example greedy logic
}

greedyAlgorithmExample();
using System;

class Program {
    static void Main() {
        GreedyAlgorithmExample();
    }

    static void GreedyAlgorithmExample() {
        // Example greedy logic
    }
}

In JavaScript, implementing a greedy algorithm involves iterating over the data while making optimal choices based on current conditions. This can reduce complexity significantly in certain scenarios.

Complexity

Time Complexity: O(n log n)
Space Complexity: O(1)

Try this approach in the editor

Approach 3: Memoization

We define a hash table d to store the allowed triangular patterns, where the key is a pair of two characters and the value is the corresponding list of characters, indicating that the two characters can form a triangular pattern with each item in the value list being the top of the triangle.

Starting from the bottom layer, for every two adjacent characters in each layer, if they can form a triangular pattern, we add the top character of the triangular pattern to the character list at the corresponding position in the next layer, then recursively process the next layer.

When the recursion reaches a single character, it means we have successfully built to the top of the pyramid, and we return true. If at some layer, two adjacent characters cannot form a triangular pattern, we return false.

function pyramidTransition(bottom: string, allowed: string[]): boolean {
    const d: number[][] = Array.from({ length: 7 }, () => Array(7).fill(0));
    for (const s of allowed) {
        const a = s.charCodeAt(0) - 65;
        const b = s.charCodeAt(1) - 65;
        const c = s.charCodeAt(2) - 65;
        d[a][b] |= 1 << c;
    }

    const f = new Map<string, boolean>();

    const dfs = (s: string, t: string[]): boolean => {
        if (s.length === 1) return true;
        if (t.length + 1 === s.length) {
            return dfs(t.join(''), []);
        }

        const key = s + '.' + t.join('');
        if (f.has(key)) return f.get(key)!;

        const i = t.length;
        const a = s.charCodeAt(i) - 65;
        const b = s.charCodeAt(i + 1) - 65;
        let cs = d[a][b];

        for (let c = 0; c < 7; c++) {
            if ((cs >> c) & 1) {
                t.push(String.fromCharCode(65 + c));
                if (dfs(s, t)) {
                    f.set(key, true);
                    return true;
                }
                t.pop();
            }
        }

        f.set(key, false);
        return false;
    };

    return dfs(bottom, []);
}
func pyramidTransition(bottom string, allowed []string) bool {
	d := make([][]int, 7)
	for i := 0; i < 7; i++ {
		d[i] = make([]int, 7)
	}

	for _, s := range allowed {
		a := int(s[0] - 'A')
		b := int(s[1] - 'A')
		c := int(s[2] - 'A')
		d[a][b] |= 1 << c
	}

	f := make(map[string]bool)

	var dfs func(s string, t []byte) bool
	dfs = func(s string, t []byte) bool {
		if len(s) == 1 {
			return true
		}
		if len(t)+1 == len(s) {
			return dfs(string(t), []byte{})
		}

		key := s + "." + string(t)
		if v, ok := f[key]; ok {
			return v
		}

		i := len(t)
		a := int(s[i] - 'A')
		b := int(s[i+1] - 'A')
		cs := d[a][b]

		for c := 0; c < 7; c++ {
			if (cs>>c)&1 == 1 {
				t = append(t, byte('A'+c))
				if dfs(s, t) {
					f[key] = true
					return true
				}
				t = t[:len(t)-1]
			}
		}

		f[key] = false
		return false
	}

	return dfs(bottom, []byte{})
}
use std::collections::HashMap;

impl Solution {
    pub fn pyramid_transition(bottom: String, allowed: Vec<String>) -> bool {
        let mut d = vec![vec![0; 7]; 7];
        for s in allowed {
            let a = (s.as_bytes()[0] - b'A') as usize;
            let b = (s.as_bytes()[1] - b'A') as usize;
            let c = (s.as_bytes()[2] - b'A') as usize;
            d[a][b] |= 1 << c;
        }

        let mut f = HashMap::<String, bool>::new();

        fn dfs(s: &str, t: &mut Vec<u8>, d: &Vec<Vec<i32>>, f: &mut HashMap<String, bool>) -> bool {
            if s.len() == 1 {
                return true;
            }

            if t.len() + 1 == s.len() {
                let next = String::from_utf8_lossy(t).to_string();
                let mut nt = Vec::new();
                return dfs(&next, &mut nt, d, f);
            }

            let key = format!("{}.{}", s, String::from_utf8_lossy(t));
            if let Some(&res) = f.get(&key) {
                return res;
            }

            let i = t.len();
            let a = (s.as_bytes()[i] - b'A') as usize;
            let b = (s.as_bytes()[i + 1] - b'A') as usize;
            let mut cs = d[a][b];

            for c in 0..7 {
                if (cs >> c) & 1 == 1 {
                    t.push(b'A' + c as u8);
                    if dfs(s, t, d, f) {
                        f.insert(key.clone(), true);
                        t.pop();
                        return true;
                    }
                    t.pop();
                }
            }

            f.insert(key, false);
            false
        }

        let mut t = Vec::new();
        dfs(&bottom, &mut t, &d, &mut f)
    }
}
Try this approach in the editor

Complexity comparison

ApproachComplexity
Dynamic Programming Approach

Time Complexity: O(n^2)
Space Complexity: O(n)

Greedy Algorithm

Time Complexity: O(n log n)
Space Complexity: O(1)

Memoization–

Complexity analysis

ApproachTimeSpaceWhen to Use
DFS with Memoization (DP on Rows)O(2^n)O(2^n)General case; easiest to implement and common in interviews
Bitmask Optimized DFSO(2^n)O(26^2)When transitions are many and faster candidate checks are needed
Greedy Branch PruningO(2^n)O(n)Useful when early invalid pairs eliminate large parts of the search tree

Frequently asked questions

Is Pyramid Transition Matrix easy or hard?

Pyramid Transition Matrix is rated Medium difficulty. The challenge is modeling the pyramid-building process and managing the exponential branching efficiently using memoization or pruning techniques.

Pyramid Transition Matrix Python/Java solution

Typical Python or Java solutions preprocess the allowed triples into a map from pair → list of possible blocks. A recursive DFS builds the next row character by character while caching failed rows. Memoization ensures repeated states are skipped, keeping the solution efficient within constraints.

How to solve Pyramid Transition Matrix in O(n)?

An O(n) solution does not exist for the general case because the algorithm must explore combinations of possible rows. The closest practical optimization uses bitmasks to represent valid transitions and prune branches quickly. This reduces constant factors but the theoretical worst case remains exponential.

What is the best approach for Pyramid Transition Matrix?

The most reliable approach is DFS with memoization. Build a mapping from each pair of characters to all possible blocks that can sit above them, then recursively construct the next row. Memoizing previously failed rows avoids recomputation and significantly reduces the search space. The worst‑case time complexity remains exponential but works efficiently for the given constraints.

Is Pyramid Transition Matrix asked at Google/Amazon/Meta?

Pyramid Transition Matrix appears in interview preparation sets for companies like Google, Amazon, and Meta because it tests recursion, state pruning, and graph-style search. The problem evaluates how well you model combinational states and apply DFS with memoization.

What data structure is used in Pyramid Transition Matrix?

The core structure is a hash map that maps each pair of characters to possible top characters. During the search, recursion or a stack performs DFS over possible rows. Some optimized solutions also use bitmasks to store transitions compactly.

What is the time complexity of Pyramid Transition Matrix?

The worst‑case time complexity is O(2^n) because each level of the pyramid can branch into multiple candidate rows. With memoization, many invalid configurations are cached and skipped, which improves practical performance. Space complexity is also O(2^n) due to recursion depth and stored states.

Comments

Now write it yourselfClose this page and solve Pyramid Transition Matrix in the editor, without looking.
Practise Pyramid Transition Matrix