|
1 | 1 | package g0701_0800.s0770_basic_calculator_iv;
|
2 | 2 |
|
3 | 3 | // #Hard #String #Hash_Table #Math #Stack #Recursion
|
4 |
| -// #2022_03_26_Time_17_ms_(66.18%)_Space_45.8_MB_(60.29%) |
| 4 | +// #2022_04_30_Time_8_ms_(96.92%)_Space_42.9_MB_(93.85%) |
5 | 5 |
|
6 | 6 | import java.util.ArrayList;
|
7 | 7 | import java.util.Collections;
|
8 | 8 | import java.util.HashMap;
|
9 |
| -import java.util.LinkedList; |
10 | 9 | import java.util.List;
|
11 | 10 | import java.util.Map;
|
12 | 11 |
|
13 |
| -@SuppressWarnings("java:S1104") |
14 | 12 | public class Solution {
|
15 |
| - public List<String> basicCalculatorIV(String expression, String[] evalvars, int[] evalints) { |
16 |
| - Map<String, Integer> knownVars = new HashMap<>(); |
17 |
| - for (int i = 0; i < evalvars.length; i++) { |
18 |
| - knownVars.put(evalvars[i], evalints[i]); |
19 |
| - } |
20 |
| - LinkedList<Expr> expressions = new LinkedList<>(); |
21 |
| - LinkedList<String> ops = new LinkedList<>(); |
22 |
| - for (String token : parseExpression(expression)) { |
23 |
| - if (Character.isDigit(token.charAt(0))) { |
24 |
| - expressions.push(new Expr("", Integer.parseInt(token))); |
25 |
| - } else if (token.equals("(")) { |
26 |
| - ops.push("("); |
27 |
| - } else if (token.equals(")")) { |
28 |
| - while (!ops.peek().equals("(")) { |
29 |
| - doOneEval(ops, expressions); |
| 13 | + private static class Result { |
| 14 | + private Map<List<String>, Integer> map; |
| 15 | + |
| 16 | + Result() { |
| 17 | + map = new HashMap<>(); |
| 18 | + } |
| 19 | + |
| 20 | + Result(Map<List<String>, Integer> map) { |
| 21 | + this.map = map; |
| 22 | + } |
| 23 | + |
| 24 | + void update(List<String> key, int val) { |
| 25 | + map.put(key, map.getOrDefault(key, 0) + val); |
| 26 | + } |
| 27 | + |
| 28 | + Map<List<String>, Integer> getMap() { |
| 29 | + return map; |
| 30 | + } |
| 31 | + |
| 32 | + List<String> toList() { |
| 33 | + List<List<String>> keyList = new ArrayList<>(map.keySet()); |
| 34 | + Map<List<String>, String> list2String = new HashMap<>(); |
| 35 | + for (List<String> key : keyList) { |
| 36 | + StringBuilder sb = new StringBuilder(); |
| 37 | + for (String k : key) { |
| 38 | + sb.append(k).append("*"); |
30 | 39 | }
|
31 |
| - ops.pop(); |
32 |
| - } else if (token.equals("+") || token.equals("-") || token.equals("*")) { |
33 |
| - int rank = getRank(token); |
34 |
| - while (!ops.isEmpty() && !ops.peek().equals("(") && getRank(ops.peek()) >= rank) { |
35 |
| - doOneEval(ops, expressions); |
| 40 | + list2String.put(key, sb.toString()); |
| 41 | + } |
| 42 | + keyList.sort( |
| 43 | + (a, b) -> |
| 44 | + (a.size() == b.size() |
| 45 | + ? list2String.get(a).compareTo(list2String.get(b)) |
| 46 | + : b.size() - a.size())); |
| 47 | + List<String> res = new ArrayList<>(); |
| 48 | + for (List<String> key : keyList) { |
| 49 | + if (map.get(key) == 0) { |
| 50 | + continue; |
36 | 51 | }
|
37 |
| - ops.push(token); |
38 |
| - } else if (knownVars.containsKey(token)) { |
39 |
| - expressions.push(new Expr("", knownVars.get(token))); |
40 |
| - } else { |
41 |
| - expressions.push(new Expr(token, 1)); |
| 52 | + StringBuilder sb = new StringBuilder(); |
| 53 | + sb.append(map.get(key)); |
| 54 | + for (String k : key) { |
| 55 | + sb.append("*").append(k); |
| 56 | + } |
| 57 | + res.add(sb.toString()); |
42 | 58 | }
|
| 59 | + return res; |
43 | 60 | }
|
44 |
| - while (!ops.isEmpty()) { |
45 |
| - doOneEval(ops, expressions); |
| 61 | + } |
| 62 | + |
| 63 | + private Map<String, Integer> evalMap; |
| 64 | + private int i = 0; |
| 65 | + |
| 66 | + public List<String> basicCalculatorIV(String expression, String[] evalvars, int[] evalints) { |
| 67 | + evalMap = new HashMap<>(); |
| 68 | + for (int j = 0; j < evalvars.length; j++) { |
| 69 | + evalMap.put(evalvars[j], evalints[j]); |
46 | 70 | }
|
47 |
| - Expr expr = expressions.peek(); |
48 |
| - List<String> output = new ArrayList<>(); |
49 |
| - for (String term : expr.terms.keySet()) { |
50 |
| - if (expr.terms.get(term) != 0) { |
51 |
| - output.add("" + expr.terms.get(term) + (term.equals("") ? "" : "*" + term)); |
| 71 | + i = -1; |
| 72 | + next(expression); |
| 73 | + Result res = expression(expression); |
| 74 | + return res.toList(); |
| 75 | + } |
| 76 | + |
| 77 | + private Result expression(String s) { |
| 78 | + Result res = term(s); |
| 79 | + while (i < s.length() && (s.charAt(i) == '+' || s.charAt(i) == '-')) { |
| 80 | + int c = s.charAt(i); |
| 81 | + next(s); |
| 82 | + if (c == '+') { |
| 83 | + res = add(res, term(s)); |
| 84 | + } else { |
| 85 | + res = subtract(res, term(s)); |
52 | 86 | }
|
53 | 87 | }
|
54 |
| - output.sort( |
55 |
| - (a, b) -> { |
56 |
| - int aStar = 0; |
57 |
| - int bStar = 0; |
58 |
| - for (int i = 0; i < a.length(); i++) { |
59 |
| - if (a.charAt(i) == '*') { |
60 |
| - aStar++; |
61 |
| - } |
62 |
| - } |
63 |
| - for (int i = 0; i < b.length(); i++) { |
64 |
| - if (b.charAt(i) == '*') { |
65 |
| - bStar++; |
66 |
| - } |
67 |
| - } |
68 |
| - if (aStar != bStar) { |
69 |
| - return bStar - aStar; |
70 |
| - } |
71 |
| - return a.split("\\*", 2)[1].compareTo(b.split("\\*", 2)[1]); |
72 |
| - }); |
73 |
| - return output; |
| 88 | + return res; |
74 | 89 | }
|
75 | 90 |
|
76 |
| - private int getRank(String op) { |
77 |
| - if (op.equals("+") || op.equals("-")) { |
78 |
| - return 1; |
| 91 | + private Result term(String s) { |
| 92 | + Result res = factor(s); |
| 93 | + while (i < s.length() && s.charAt(i) == '*') { |
| 94 | + next(s); |
| 95 | + res = multiply(res, factor(s)); |
79 | 96 | }
|
80 |
| - // * |
81 |
| - return 2; |
| 97 | + return res; |
82 | 98 | }
|
83 | 99 |
|
84 |
| - private List<String> parseExpression(String s) { |
85 |
| - List<String> output = new ArrayList<>(); |
86 |
| - for (String token : s.split(" ")) { |
87 |
| - int opening = 0; |
88 |
| - for (; token.charAt(opening) == '('; opening++) { |
89 |
| - output.add("("); |
90 |
| - } |
91 |
| - int closing = 0; |
92 |
| - while (token.charAt(token.length() - 1 - closing) == ')') { |
93 |
| - closing++; |
94 |
| - } |
95 |
| - output.add(token.substring(opening, token.length() - closing)); |
96 |
| - while (closing-- > 0) { |
97 |
| - output.add(")"); |
| 100 | + private Result multiply(Result r1, Result r2) { |
| 101 | + Map<List<String>, Integer> map1 = r1.getMap(); |
| 102 | + Map<List<String>, Integer> map2 = r2.getMap(); |
| 103 | + Map<List<String>, Integer> map = new HashMap<>(); |
| 104 | + for (Map.Entry<List<String>, Integer> entry1 : map1.entrySet()) { |
| 105 | + for (Map.Entry<List<String>, Integer> entry2 : map2.entrySet()) { |
| 106 | + List<String> key = new ArrayList<>(entry1.getKey()); |
| 107 | + key.addAll(entry2.getKey()); |
| 108 | + Collections.sort(key); |
| 109 | + map.put(key, map.getOrDefault(key, 0) + entry1.getValue() * entry2.getValue()); |
98 | 110 | }
|
99 | 111 | }
|
100 |
| - return output; |
| 112 | + return new Result(map); |
101 | 113 | }
|
102 | 114 |
|
103 |
| - private void doOneEval(LinkedList<String> ops, LinkedList<Expr> expressions) { |
104 |
| - Expr e2 = expressions.pop(); |
105 |
| - Expr e1 = expressions.pop(); |
106 |
| - Expr res = new Expr("", 0); |
107 |
| - String op = ops.pop(); |
108 |
| - if (op.equals("+") || op.equals("-")) { |
109 |
| - int sign = op.equals("-") ? -1 : 1; |
110 |
| - res.terms = e1.terms; |
111 |
| - for (String term : e2.terms.keySet()) { |
112 |
| - res.terms.put(term, sign * e2.terms.get(term) + res.terms.getOrDefault(term, 0)); |
113 |
| - } |
114 |
| - } else { |
115 |
| - // * |
116 |
| - for (String t1 : e1.terms.keySet()) { |
117 |
| - for (String t2 : e2.terms.keySet()) { |
118 |
| - String resTerm = generateTerm(t1, t2); |
119 |
| - res.terms.put( |
120 |
| - resTerm, |
121 |
| - e1.terms.get(t1) * e2.terms.get(t2) |
122 |
| - + res.terms.getOrDefault(resTerm, 0)); |
123 |
| - } |
124 |
| - } |
| 115 | + private Result add(Result r1, Result r2) { |
| 116 | + Map<List<String>, Integer> map1 = r1.getMap(); |
| 117 | + Map<List<String>, Integer> map2 = r2.getMap(); |
| 118 | + Map<List<String>, Integer> map = new HashMap<>(); |
| 119 | + for (Map.Entry<List<String>, Integer> entry1 : map1.entrySet()) { |
| 120 | + map.put(entry1.getKey(), map.getOrDefault(entry1.getKey(), 0) + entry1.getValue()); |
| 121 | + } |
| 122 | + for (Map.Entry<List<String>, Integer> entry2 : map2.entrySet()) { |
| 123 | + map.put(entry2.getKey(), map.getOrDefault(entry2.getKey(), 0) + entry2.getValue()); |
125 | 124 | }
|
126 |
| - expressions.push(res); |
| 125 | + return new Result(map); |
127 | 126 | }
|
128 | 127 |
|
129 |
| - private String generateTerm(String t1, String t2) { |
130 |
| - if (t1.equals("")) { |
131 |
| - return t2; |
| 128 | + private Result subtract(Result r1, Result r2) { |
| 129 | + Map<List<String>, Integer> map1 = r1.getMap(); |
| 130 | + Map<List<String>, Integer> map2 = r2.getMap(); |
| 131 | + Map<List<String>, Integer> map = new HashMap<>(); |
| 132 | + for (Map.Entry<List<String>, Integer> entry1 : map1.entrySet()) { |
| 133 | + map.put(entry1.getKey(), map.getOrDefault(entry1.getKey(), 0) + entry1.getValue()); |
132 | 134 | }
|
133 |
| - if (t2.equals("")) { |
134 |
| - return t1; |
| 135 | + for (Map.Entry<List<String>, Integer> entry2 : map2.entrySet()) { |
| 136 | + map.put(entry2.getKey(), map.getOrDefault(entry2.getKey(), 0) - entry2.getValue()); |
135 | 137 | }
|
136 |
| - List<String> parts = new ArrayList<>(); |
137 |
| - Collections.addAll(parts, t1.split("\\*")); |
138 |
| - Collections.addAll(parts, t2.split("\\*")); |
139 |
| - Collections.sort(parts); |
| 138 | + return new Result(map); |
| 139 | + } |
| 140 | + |
| 141 | + private Result factor(String s) { |
| 142 | + Result res = new Result(); |
| 143 | + if (s.charAt(i) == '(') { |
| 144 | + next(s); |
| 145 | + res = expression(s); |
| 146 | + next(s); |
| 147 | + return res; |
| 148 | + } |
| 149 | + if (Character.isLowerCase(s.charAt(i))) { |
| 150 | + return identifier(s); |
| 151 | + } |
| 152 | + res.update(new ArrayList<>(), number(s)); |
| 153 | + return res; |
| 154 | + } |
| 155 | + |
| 156 | + private Result identifier(String s) { |
| 157 | + Result res = new Result(); |
140 | 158 | StringBuilder sb = new StringBuilder();
|
141 |
| - for (String part : parts) { |
142 |
| - sb.append(part + "*"); |
| 159 | + while (i < s.length() && Character.isLowerCase(s.charAt(i))) { |
| 160 | + sb.append(s.charAt(i)); |
| 161 | + i++; |
143 | 162 | }
|
144 |
| - if (sb.length() > 0) { |
145 |
| - sb.setLength(sb.length() - 1); |
| 163 | + i--; |
| 164 | + next(s); |
| 165 | + String variable = sb.toString(); |
| 166 | + if (evalMap.containsKey(variable)) { |
| 167 | + res.update(new ArrayList<>(), evalMap.get(variable)); |
| 168 | + } else { |
| 169 | + List<String> key = new ArrayList<>(); |
| 170 | + key.add(variable); |
| 171 | + res.update(key, 1); |
146 | 172 | }
|
147 |
| - return sb.toString(); |
| 173 | + return res; |
148 | 174 | }
|
149 | 175 |
|
150 |
| - private static class Expr { |
151 |
| - public Map<String, Integer> terms; |
| 176 | + private int number(String s) { |
| 177 | + int res = 0; |
| 178 | + while (i < s.length() && s.charAt(i) >= '0' && s.charAt(i) <= '9') { |
| 179 | + res = res * 10 + (s.charAt(i) - '0'); |
| 180 | + i++; |
| 181 | + } |
| 182 | + i--; |
| 183 | + next(s); |
| 184 | + return res; |
| 185 | + } |
152 | 186 |
|
153 |
| - public Expr(String term, int val) { |
154 |
| - terms = new HashMap<>(); |
155 |
| - terms.put(term, val); |
| 187 | + private void next(String s) { |
| 188 | + i++; |
| 189 | + while (i < s.length() && s.charAt(i) == ' ') { |
| 190 | + i++; |
156 | 191 | }
|
157 | 192 | }
|
158 | 193 | }
|
0 commit comments