Leetcode 756. Pyramid Transition Matrix
You are given a string bottom
, which represents the bottom row of a pyramid. You are also given a list of strings allowed
, where each string represents a valid triple that can be used to build the pyramid from the bottom row.
You can use these triples to place a block on top of two adjacent blocks in the current row. Specifically, if you have blocks A
and B
in the current row (with A
to the left of B
), and there is a triple “ABC” in allowed
, then you can place block C
on top of blocks A
and B
.
Return true
if you can build the pyramid from the bottom row to the top such that it results in a single block at the top. Otherwise, return false
.
Input: bottom = "BCD", allowed = ["BCG", "CDE", "GEA", "FFF"]
Output: true
Explanation:
A
/ \
G E
/ \ / \
B C D
Input: bottom = "AABA", allowed = ["AAA", "AAB", "ABA", "ABB", "BAC"]
Output: false
allowed
triples always represent valid block configurations?bottom
row contain any length of string, and does it always consist of uppercase letters?bottom
row and allowed
list become? (For time complexity estimation)allowed
list into a dictionary where keys are pairs (A, B)
and values are lists of valid third blocks C
.true
since we built the pyramid.import java.util.*;
public class PyramidTransition {
public boolean pyramidTransition(String bottom, List<String> allowed) {
// Build the map from pairs to possible tops
Map<String, Set<Character>> map = new HashMap<>();
for (String triple : allowed) {
String base = triple.substring(0, 2);
char top = triple.charAt(2);
map.putIfAbsent(base, new HashSet<>());
map.get(base).add(top);
}
// Memoization map
Map<String, Boolean> memo = new HashMap<>();
// Function to recursively check if we can build the pyramid
return canBuildPyramid(bottom, map, memo);
}
private boolean canBuildPyramid(String bottom, Map<String, Set<Character>> map, Map<String, Boolean> memo) {
if (bottom.length() == 1) {
return true; // A single block as the top means we've successfully built the pyramid
}
// Check memo
if (memo.containsKey(bottom)) {
return memo.get(bottom);
}
// Attempt to build the next level
List<String> nextLevels = new ArrayList<>();
generateNextLevels(bottom, new StringBuilder(), 0, map, nextLevels);
for (String nextLevel : nextLevels) {
if (canBuildPyramid(nextLevel, map, memo)) {
memo.put(bottom, true);
return true;
}
}
memo.put(bottom, false);
return false;
}
private void generateNextLevels(String bottom, StringBuilder current, int index, Map<String, Set<Character>> map, List<String> nextLevels) {
if (index == bottom.length() - 1) {
nextLevels.add(current.toString());
return;
}
String base = bottom.substring(index, index + 2);
if (map.containsKey(base)) {
for (char top : map.get(base)) {
current.append(top);
generateNextLevels(bottom, current, index + 1, map, nextLevels);
current.deleteCharAt(current.length() - 1); // Backtrack
}
}
}
public static void main(String[] args) {
PyramidTransition ptm = new PyramidTransition();
String bottom1 = "BCD";
List<String> allowed1 = Arrays.asList("BCG", "CDE", "GEA", "FFF");
System.out.println(ptm.pyramidTransition(bottom1, allowed1)); // true
String bottom2 = "AABA";
List<String> allowed2 = Arrays.asList("AAA", "AAB", "ABA", "ABB", "BAC");
System.out.println(ptm.pyramidTransition(bottom2, allowed2)); // false
}
}
O(n^2)
for memoization and O(k)
for the dictionary of triples, where n
is the length of bottom
and k
is the size of allowed
.bottom
. However, memoization prunes many subproblems making the approach practical for reasonable input limits.Make sure to check the constraints for very large inputs to ensure the solution is feasible within the given limits.
Got blindsided by a question you didn’t expect?
Spend too much time studying?
Or simply don’t have the time to go over all 3000 questions?