LeetCode Solutions
770. Basic Calculator IV
Time: $O(n)$ Space: $O(n)$
class Poly {
friend Poly operator+(const Poly& lhs, const Poly& rhs) {
Poly res(lhs);
for (const auto& [term, coef] : rhs.terms)
res.terms[term] += coef;
return res;
}
friend Poly operator-(const Poly& lhs, const Poly& rhs) {
Poly res(lhs);
for (const auto& [term, coef] : rhs.terms)
res.terms[term] -= coef;
return res;
}
friend Poly operator*(const Poly& lhs, const Poly& rhs) {
Poly res;
for (const auto& [a, aCoef] : lhs.terms)
for (const auto& [b, bCoef] : rhs.terms)
res.terms[merge(a, b)] += aCoef * bCoef;
return res;
}
// Friend ostream& operator<<(ostream& os, const Poly& poly) {
// os << "{";
// for (const auto& [term, coef] : poly.terms)
// os << term << ": " << coef << ", ";
// os << "}";
// return os;
// }
public:
vector<string> toList() {
vector<string> res;
vector<string> keys;
for (const auto& [term, _] : terms)
keys.push_back(term);
sort(begin(keys), end(keys), [&](const auto& a, const auto& b) {
// Smallest degree is the last
if (a == "1")
return false;
if (b == "1")
return true;
const vector<string> as = split(a, '*');
const vector<string> bs = split(b, '*');
// Largest degree is the first
// Breaking ties by lexicographic order
return as.size() == bs.size() ? a < b : as.size() > bs.size();
});
auto concat = [&](const string& term) -> string {
if (term == "1")
return to_string(terms[term]);
return to_string(terms[term]) + '*' + term;
};
for (const string& key : keys)
if (terms[key])
res.push_back(concat(key));
return res;
}
Poly() = default;
Poly(const string& term, int coef) {
terms[term] = coef;
}
private:
unordered_map<string, int> terms;
// E.g. merge("a*b", "a*c") -> "a*a*b*c"
static string merge(const string& a, const string& b) {
if (a == "1")
return b;
if (b == "1")
return a;
string res;
vector<string> A = split(a, '*');
vector<string> B = split(b, '*');
int i = 0; // A's index
int j = 0; // B's index
while (i < A.size() && j < B.size())
if (A[i] < B[j])
res += '*' + A[i++];
else
res += '*' + B[j++];
while (i < A.size())
res += '*' + A[i++];
while (j < B.size())
res += '*' + B[j++];
return res.substr(1);
}
static vector<string> split(const string& token, char c) {
vector<string> vars;
istringstream iss(token);
for (string var; getline(iss, var, c);)
vars.push_back(var);
return vars;
}
};
class Solution {
public:
vector<string> basicCalculatorIV(string expression, vector<string>& evalvars,
vector<int>& evalints) {
vector<string> tokens = getTokens(expression);
unordered_map<string, int> evalMap;
for (int i = 0; i < evalvars.size(); ++i)
evalMap[evalvars[i]] = evalints[i];
for (string& token : tokens)
if (evalMap.count(token))
token = to_string(evalMap[token]);
const vector<string>& postfix = infixToPostfix(tokens);
return evaluate(postfix).toList();
}
private:
vector<string> getTokens(const string& s) {
vector<string> tokens;
int i = 0;
for (int j = 0; j < s.length(); ++j)
if (s[j] == ' ') {
if (i < j)
tokens.push_back(s.substr(i, j - i));
i = j + 1;
} else if (string("()+-*").find(s[j]) != string::npos) {
if (i < j)
tokens.push_back(s.substr(i, j - i));
tokens.push_back(s.substr(j, 1));
i = j + 1;
}
if (i < s.length())
tokens.push_back(s.substr(i));
return tokens;
}
bool isOperator(const string& token) {
return token == "+" || token == "-" || token == "*";
}
vector<string> infixToPostfix(const vector<string>& tokens) {
vector<string> postfix;
stack<string> ops;
auto precedes = [](const string& prevOp, const string& currOp) -> bool {
if (prevOp == "(")
return false;
return prevOp == "*" || currOp == "+" || currOp == "-";
};
for (const string& token : tokens)
if (token == "(") {
ops.push(token);
} else if (token == ")") {
while (ops.top() != "(")
postfix.push_back(ops.top()), ops.pop();
ops.pop();
} else if (isOperator(token)) {
while (!ops.empty() && precedes(ops.top(), token))
postfix.push_back(ops.top()), ops.pop();
ops.push(token);
} else { // IsOperand(token)
postfix.push_back(token);
}
while (!ops.empty())
postfix.push_back(ops.top()), ops.pop();
return postfix;
}
Poly evaluate(const vector<string>& postfix) {
vector<Poly> polys;
for (const string& token : postfix)
if (isOperator(token)) {
const Poly b = polys.back();
polys.pop_back();
const Poly a = polys.back();
polys.pop_back();
if (token == "+")
polys.push_back(a + b);
else if (token == "-")
polys.push_back(a - b);
else // Token == "*"
polys.push_back(a * b);
} else if (token[0] == '-' || all_of(begin(token), end(token),
[](char c) { return isdigit(c); })) {
polys.push_back(Poly("1", stoi(token)));
} else {
polys.push_back(Poly(token, 1));
}
return polys[0];
}
};
class Poly {
public Poly add(Poly o) {
for (final String term : o.terms.keySet())
terms.merge(term, o.terms.get(term), Integer::sum);
return this;
}
public Poly minus(Poly o) {
for (final String term : o.terms.keySet())
terms.merge(term, -o.terms.get(term), Integer::sum);
return this;
}
public Poly mult(Poly o) {
Poly res = new Poly();
for (final String a : terms.keySet())
for (final String b : o.terms.keySet())
res.terms.merge(merge(a, b), terms.get(a) * o.terms.get(b), Integer::sum);
return res;
}
// @Override
// Public String toString() {
// StringBuilder sb = new StringBuilder();
// sb.append("{");
// for (final String term : terms.keySet())
// sb.append(term).append(": ").append(terms.get(term)).append(", ");
// sb.append("}");
// return sb.toString();
// }
public List<String> toList() {
List<String> res = new ArrayList<>();
List<String> keys = new ArrayList<>(terms.keySet());
Collections.sort(keys, new Comparator<String>() {
@Override
public int compare(final String a, final String b) {
// Smallest degree is the last
if (a.equals("1"))
return 1;
if (b.equals("1"))
return -1;
String[] as = a.split("\\*");
String[] bs = b.split("\\*");
// Largest degree is the first
// Breaking ties by lexicographic order
return as.length == bs.length ? a.compareTo(b) : bs.length - as.length;
}
});
for (final String key : keys)
if (terms.get(key) != 0)
res.add(concat(key));
return res;
}
public Poly() {}
public Poly(final String term, int coef) {
terms.put(term, coef);
}
private Map<String, Integer> terms = new HashMap<>();
// E.g. merge("a*b", "a*c") -> "a*a*b*c"
private static String merge(final String a, final String b) {
if (a.equals("1"))
return b;
if (b.equals("1"))
return a;
StringBuilder sb = new StringBuilder();
String[] A = a.split("\\*");
String[] B = b.split("\\*");
int i = 0; // A's index
int j = 0; // B's index
while (i < A.length && j < B.length)
if (A[i].compareTo(B[j]) < 0)
sb.append("*").append(A[i++]);
else
sb.append("*").append(B[j++]);
while (i < A.length)
sb.append("*").append(A[i++]);
while (j < B.length)
sb.append("*").append(B[j++]);
return sb.substring(1).toString();
}
private String concat(final String term) {
if (term.equals("1"))
return String.valueOf(terms.get(term));
return new StringBuilder().append(terms.get(term)).append('*').append(term).toString();
}
}
class Solution {
public List<String> basicCalculatorIV(String expression, String[] evalvars, int[] evalints) {
List<String> tokens = getTokens(expression);
Map<String, Integer> evalMap = new HashMap<>();
for (int i = 0; i < evalvars.length; ++i)
evalMap.put(evalvars[i], evalints[i]);
for (int i = 0; i < tokens.size(); ++i)
if (evalMap.containsKey(tokens.get(i)))
tokens.set(i, String.valueOf(evalMap.get(tokens.get(i))));
List<String> postfix = infixToPostfix(tokens);
return evaluate(postfix).toList();
}
private List<String> getTokens(final String s) {
List<String> tokens = new ArrayList<>();
int i = 0;
for (int j = 0; j < s.length(); ++j)
if (s.charAt(j) == ' ') {
if (i < j)
tokens.add(s.substring(i, j));
i = j + 1;
} else if ("()+-*".contains(s.substring(j, j + 1))) {
if (i < j)
tokens.add(s.substring(i, j));
tokens.add(s.substring(j, j + 1));
i = j + 1;
}
if (i < s.length())
tokens.add(s.substring(i));
return tokens;
}
private boolean isOperator(final String token) {
return token.equals("+") || token.equals("-") || token.equals("*");
}
private boolean precedes(final String prevOp, final String currOp) {
if (prevOp.equals("("))
return false;
return prevOp.equals("*") || currOp.equals("+") || currOp.equals("-");
}
private List<String> infixToPostfix(List<String> tokens) {
List<String> postfix = new ArrayList<>();
Deque<String> ops = new ArrayDeque<>();
for (final String token : tokens)
if (token.equals("(")) {
ops.push(token);
} else if (token.equals(")")) {
while (!ops.peek().equals("("))
postfix.add(ops.pop());
ops.pop();
} else if (isOperator(token)) {
while (!ops.isEmpty() && precedes(ops.peek(), token))
postfix.add(ops.pop());
ops.push(token);
} else { // IsOperand(token)
postfix.add(token);
}
while (!ops.isEmpty())
postfix.add(ops.pop());
return postfix;
}
private Poly evaluate(List<String> postfix) {
LinkedList<Poly> polys = new LinkedList<>();
for (final String token : postfix)
if (isOperator(token)) {
final Poly b = polys.removeLast();
final Poly a = polys.removeLast();
if (token.equals("+"))
polys.add(a.add(b));
else if (token.equals("-"))
polys.add(a.minus(b));
else // Token == "*"
polys.add(a.mult(b));
} else if (token.charAt(0) == '-' || token.chars().allMatch(c -> Character.isDigit(c))) {
polys.add(new Poly("1", Integer.parseInt(token)));
} else {
polys.add(new Poly(token, 1));
}
return polys.getFirst();
}
}
class Poly:
def __init__(self, term: str = None, coef: int = None):
if term and coef:
self.terms = Counter({term: coef})
else:
self.terms = Counter()
def __add__(self, other):
for term, coef in other.terms.items():
self.terms[term] += coef
return self
def __sub__(self, other):
for term, coef in other.terms.items():
self.terms[term] -= coef
return self
def __mul__(self, other):
res = Poly()
for a, aCoef in self.terms.items():
for b, bCoef in other.terms.items():
res.terms[self._merge(a, b)] += aCoef * bCoef
return res
# Def __str__(self):
# res = []
# for term, coef in self.terms.items():
# res.append(term + ': ' + str(coef))
# return '{' + ', '.join(res) + '}'
def toList(self) -> List[str]:
for term in list(self.terms.keys()):
if not self.terms[term]:
del self.terms[term]
def cmp(term: str) -> tuple:
# Smallest degree is the last
if term == '1':
return (0,)
var = term.split('*')
# Largest degree is the first
# Breaking ties by lexicographic order
return (-len(var), term)
def concat(term: str) -> str:
if term == '1':
return str(self.terms[term])
return str(self.terms[term]) + '*' + term
terms = list(self.terms.keys())
terms.sort(key=cmp)
return [concat(term) for term in terms]
def _merge(self, a: str, b: str) -> str:
if a == '1':
return b
if b == '1':
return a
res = []
A = a.split('*')
B = b.split('*')
i = 0 # A's index
j = 0 # B's index
while i < len(A) and j < len(B):
if A[i] < B[j]:
res.append(A[i])
i += 1
else:
res.append(B[j])
j += 1
return '*'.join(res + A[i:] + B[j:])
class Solution:
def basicCalculatorIV(self, expression: str, evalvars: List[str], evalints: List[int]) -> List[str]:
tokens = list(self._getTokens(expression))
evalMap = {a: b for a, b in zip(evalvars, evalints)}
for i, token in enumerate(tokens):
if token in evalMap:
tokens[i] = str(evalMap[token])
postfix = self._infixToPostfix(tokens)
return self._evaluate(postfix).toList()
def _getTokens(self, s: str) -> Iterator[str]:
i = 0
for j, c in enumerate(s):
if c == ' ':
if i < j:
yield s[i:j]
i = j + 1
elif c in '()+-*':
if i < j:
yield s[i:j]
yield c
i = j + 1
if i < len(s):
yield s[i:]
def _infixToPostfix(self, tokens: List[str]) -> List[str]:
postfix = []
ops = []
def precedes(prevOp: chr, currOp: chr) -> bool:
if prevOp == '(':
return False
return prevOp == '*' or currOp in '+-'
for token in tokens:
if token == '(':
ops.append(token)
elif token == ')':
while ops[-1] != '(':
postfix.append(ops.pop())
ops.pop()
elif token in '+-*': # IsOperator(token)
while ops and precedes(ops[-1], token):
postfix.append(ops.pop())
ops.append(token)
else: # IsOperand(token)
postfix.append(token)
return postfix + ops[::-1]
def _evaluate(self, postfix: List[str]) -> Poly:
polys: List[Poly] = []
for token in postfix:
if token in '+-*':
b = polys.pop()
a = polys.pop()
if token == '+':
polys.append(a + b)
elif token == '-':
polys.append(a - b)
else: # Token == '*'
polys.append(a * b)
elif token.lstrip('-').isnumeric():
polys.append(Poly("1", int(token)))
else:
polys.append(Poly(token, 1))
return polys[0]