package com.freetymekiyan.algorithms.level.medium;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* Given a string representing arbitrarily nested ternary expressions, calculate the result of the expression. You can
* always assume that the given expression is valid and only consists of digits 0-9, ?, :, T and F (T and F represent
* True and False respectively).
* <p>
* Note:
* <p>
* The length of the given string is ≤ 10000.
* Each number will contain only one digit.
* The conditional expressions group right-to-left (as usual in most languages).
* The condition will always be either T or F. That is, the condition will never be a digit.
* The result of the expression will always evaluate to either a digit 0-9, T or F.
* Example 1:
* <p>
* Input: "T?2:3"
* <p>
* Output: "2"
* <p>
* Explanation: If true, then result is 2; otherwise result is 3.
* Example 2:
* <p>
* Input: "F?1:T?4:5"
* <p>
* Output: "4"
* <p>
* Explanation: The conditional expressions group right-to-left. Using parenthesis, it is read/evaluated as:
* <p>
* "(F ? 1 : (T ? 4 : 5))" "(F ? 1 : (T ? 4 : 5))"
* -> "(F ? 1 : 4)" or -> "(T ? 4 : 5)"
* -> "4" -> "4"
* Example 3:
* <p>
* Input: "T?T?F:5:3"
* <p>
* Output: "F"
* <p>
* Explanation: The conditional expressions group right-to-left. Using parenthesis, it is read/evaluated as:
* <p>
* "(T ? (T ? F : 5) : 3)" "(T ? (T ? F : 5) : 3)"
* -> "(T ? F : 3)" or -> "(T ? F : 5)"
* -> "F" -> "F"
* <p>
* Company Tags: Snapchat
* Tags: Depth-first Search, Stack
* Similar Problems: (M) Mini Parser
*/
public class TernaryExpressionParser {
/**
* Stack.
* Use a stack to help with evaluation.
* Push possible values into stack.
* Whenever there is a '?', pop two values from stack.
* Then get the char before '?', push evaluate result onto stack.
* <p>
* Create an array deque as stack.
* For i from the end of the expression to the start:
* | Get current character as c.
* | If c is digit or T/F, push onto stack.
* | If c is '?':
* | Pop two values from stack, left and right.
* | Get the char before '?':
* | If its 'T', return first value. Else return right.
* | If c is ':', just skip.
* Return the top of stack.
*/
public String parseTernary(String expression) {
Deque<Character> stack = new ArrayDeque<>();
for (int i = expression.length() - 1; i >= 0; i--) {
char c = expression.charAt(i);
if (Character.isDigit(c)) {
stack.push(c);
} else if (c == 'T' || c == 'F') {
stack.push(c);
} else if (c == '?') {
char left = stack.pop();
char right = stack.pop();
stack.push(expression.charAt(--i) == 'T' ? left : right);
}
}
return String.valueOf(stack.peek());
}
/**
* Recursive.
* The key is to find the correct colon to split the expression.
* Note that one '?' match with one ':' for a non-nested expression.
* So just need to find the ':' without any matching '?'.
* Use a counter. When '?', counter++; when '.', counter--.
* There is one '?' for current expression.
* So if count is 0, remember the colon's index.
*/
public String parseTernaryB(String expression) {
return String.valueOf(dfs(expression.toCharArray(), 0, expression.length() - 1));
}
/**
* DFS.
* Recurrent relation:
* The value of the input expression depends on the values of sub-expressions.
* If T, return the value of left sub-expression.
* If F, return right sub-expression.
* Base case:
* If the expression has only 1 value, return the value.
* Complete problem:
* If there is no sub-problem, deal with base case.
* If there is sub-problem, find the colon to split.
* Return by the first character. If 'T', return left value. Otherwise right value.
*/
private char dfs(char[] chs, int start, int end) {
if (start == end) {
return chs[start];
}
int count = 0, i = start;
for (; i <= end; i++) {
if (chs[i] == '?') {
count++;
} else if (chs[i] == ':') {
count--;
if (count == 0) {
break;
}
}
}
return chs[start] == 'T' ? dfs(chs, start + 2, i - 1) : dfs(chs, i + 1, end);
}
}