Skip to content

Commit 3351059

Browse files
committed
Add solution #770
1 parent 6e0121b commit 3351059

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,7 @@
584584
767|[Reorganize String](./0767-reorganize-string.js)|Medium|
585585
768|[Max Chunks To Make Sorted II](./0768-max-chunks-to-make-sorted-ii.js)|Hard|
586586
769|[Max Chunks To Make Sorted](./0769-max-chunks-to-make-sorted.js)|Medium|
587+
770|[Basic Calculator IV](./0770-basic-calculator-iv.js)|Hard|
587588
783|[Minimum Distance Between BST Nodes](./0783-minimum-distance-between-bst-nodes.js)|Easy|
588589
784|[Letter Case Permutation](./0784-letter-case-permutation.js)|Medium|
589590
790|[Domino and Tromino Tiling](./0790-domino-and-tromino-tiling.js)|Medium|

solutions/0770-basic-calculator-iv.js

+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
/**
2+
* 770. Basic Calculator IV
3+
* https://leetcode.com/problems/basic-calculator-iv/
4+
* Difficulty: Hard
5+
*
6+
* Given an expression such as expression = "e + 8 - a + 5" and an evaluation map such as {"e": 1}
7+
* (given in terms of evalvars = ["e"] and evalints = [1]), return a list of tokens representing
8+
* the simplified expression, such as ["-1*a","14"]
9+
* - An expression alternates chunks and symbols, with a space separating each chunk and symbol.
10+
* - A chunk is either an expression in parentheses, a variable, or a non-negative integer.
11+
* - A variable is a string of lowercase letters (not including digits.) Note that variables can
12+
* be multiple letters, and note that variables never have a leading coefficient or unary operator
13+
* like "2x" or "-x".
14+
*
15+
* Expressions are evaluated in the usual order: brackets first, then multiplication, then addition
16+
* and subtraction.
17+
*
18+
* - For example, expression = "1 + 2 * 3" has an answer of ["7"].
19+
*
20+
* The format of the output is as follows:
21+
* - For each term of free variables with a non-zero coefficient, we write the free variables within
22+
* a term in sorted order lexicographically.
23+
* - For example, we would never write a term like "b*a*c", only "a*b*c".
24+
* - Terms have degrees equal to the number of free variables being multiplied, counting
25+
* multiplicity. We write the largest degree terms of our answer first, breaking ties by
26+
* lexicographic order ignoring the leading coefficient of the term.
27+
* - For example, "a*a*b*c" has degree 4.
28+
* - The leading coefficient of the term is placed directly to the left with an asterisk separating
29+
* it from the variables (if they exist.) A leading coefficient of 1 is still printed.
30+
* - An example of a well-formatted answer is ["-2*a*a*a", "3*a*a*b", "3*b*b", "4*a", "5*c", "-6"].
31+
* - Terms (including constant terms) with coefficient 0 are not included.
32+
* - For example, an expression of "0" has an output of [].
33+
*
34+
* Note: You may assume that the given expression is always valid. All intermediate results will be
35+
* in the range of [-231, 231 - 1].
36+
*/
37+
38+
/**
39+
* @param {string} expression
40+
* @param {string[]} evalvars
41+
* @param {number[]} evalints
42+
* @return {string[]}
43+
*/
44+
var basicCalculatorIV = function(expression, evalvars, evalints) {
45+
const map = new Map();
46+
for (let i = 0; i < evalvars.length; i++) {
47+
map.set(evalvars[i], evalints[i]);
48+
}
49+
50+
const result = parse(expression, map);
51+
return result.toStringArray();
52+
53+
function parse(expr, map) {
54+
const tokens = tokenize(expr);
55+
return parseExpr(tokens, 0, map)[0];
56+
}
57+
58+
function tokenize(expr) {
59+
const tokens = [];
60+
let i = 0;
61+
62+
while (i < expr.length) {
63+
if (expr[i] === ' ') {
64+
i++; continue;
65+
}
66+
67+
if ('+-*()'.includes(expr[i])) {
68+
tokens.push(expr[i++]);
69+
continue;
70+
}
71+
72+
if (/[a-z]/.test(expr[i])) {
73+
let variable = '';
74+
while (i < expr.length && /[a-z]/.test(expr[i])) {
75+
variable += expr[i++];
76+
}
77+
tokens.push(variable);
78+
continue;
79+
}
80+
81+
if (/\d/.test(expr[i])) {
82+
let num = '';
83+
while (i < expr.length && /\d/.test(expr[i])) {
84+
num += expr[i++];
85+
}
86+
tokens.push(parseInt(num));
87+
continue;
88+
}
89+
90+
i++;
91+
}
92+
93+
return tokens;
94+
}
95+
96+
function parseExpr(tokens, start, map) {
97+
let [left, pos] = parseTerm(tokens, start, map);
98+
99+
while (pos < tokens.length && (tokens[pos] === '+' || tokens[pos] === '-')) {
100+
const op = tokens[pos];
101+
const [right, nextPos] = parseTerm(tokens, pos + 1, map);
102+
103+
left = op === '+' ? left.add(right) : left.subtract(right);
104+
pos = nextPos;
105+
}
106+
107+
return [left, pos];
108+
}
109+
110+
function parseTerm(tokens, start, map) {
111+
let [left, pos] = parseFactor(tokens, start, map);
112+
113+
while (pos < tokens.length && tokens[pos] === '*') {
114+
const [right, nextPos] = parseFactor(tokens, pos + 1, map);
115+
left = left.multiply(right);
116+
pos = nextPos;
117+
}
118+
119+
return [left, pos];
120+
}
121+
122+
function parseFactor(tokens, start, map) {
123+
const token = tokens[start];
124+
125+
if (token === '(') {
126+
const [expr, pos] = parseExpr(tokens, start + 1, map);
127+
return [expr, pos + 1];
128+
}
129+
130+
if (typeof token === 'string' && /[a-z]/.test(token)) {
131+
return map.has(token)
132+
? [new Expression([new Term(map.get(token))]), start + 1]
133+
: [new Expression([new Term(1, [token])]), start + 1];
134+
}
135+
136+
if (typeof token === 'number') {
137+
return [new Expression([new Term(token)]), start + 1];
138+
}
139+
140+
throw new Error(`Unexpected token: ${token}`);
141+
}
142+
};
143+
144+
class Term {
145+
constructor(coefficient = 0, variables = []) {
146+
this.coefficient = coefficient;
147+
this.variables = [...variables].sort();
148+
}
149+
150+
multiply(other) {
151+
return new Term(
152+
this.coefficient * other.coefficient,
153+
[...this.variables, ...other.variables].sort()
154+
);
155+
}
156+
157+
toString() {
158+
if (this.coefficient === 0) return '';
159+
if (this.variables.length === 0) return `${this.coefficient}`;
160+
return `${this.coefficient}*${this.variables.join('*')}`;
161+
}
162+
163+
get degree() {
164+
return this.variables.length;
165+
}
166+
167+
compare(other) {
168+
if (this.degree !== other.degree) return other.degree - this.degree;
169+
170+
for (let i = 0; i < this.degree; i++) {
171+
if (this.variables[i] !== other.variables[i]) {
172+
return this.variables[i].localeCompare(other.variables[i]);
173+
}
174+
}
175+
return 0;
176+
}
177+
}
178+
179+
class Expression {
180+
constructor(terms = []) {
181+
this.terms = terms;
182+
}
183+
184+
add(other, multiplier = 1) {
185+
const termMap = new Map();
186+
187+
for (const term of this.terms) {
188+
const key = term.variables.join('*');
189+
termMap.set(key, term);
190+
}
191+
192+
for (const term of other.terms) {
193+
const key = term.variables.join('*');
194+
if (termMap.has(key)) {
195+
termMap.get(key).coefficient += term.coefficient * multiplier;
196+
} else {
197+
const newTerm = new Term(term.coefficient * multiplier, term.variables);
198+
this.terms.push(newTerm);
199+
termMap.set(key, newTerm);
200+
}
201+
}
202+
203+
this.terms = this.terms.filter(term => term.coefficient !== 0);
204+
return this;
205+
}
206+
207+
subtract(other) {
208+
return this.add(other, -1);
209+
}
210+
211+
multiply(other) {
212+
const result = new Expression();
213+
214+
for (const term1 of this.terms) {
215+
for (const term2 of other.terms) {
216+
const product = term1.multiply(term2);
217+
if (product.coefficient !== 0) {
218+
result.add(new Expression([product]));
219+
}
220+
}
221+
}
222+
223+
return result;
224+
}
225+
226+
toStringArray() {
227+
this.terms.sort((a, b) => a.compare(b));
228+
return this.terms.map(term => term.toString()).filter(Boolean);
229+
}
230+
}

0 commit comments

Comments
 (0)