Hard
题目描述
给定一个表达式 expression 如 "e + 8 - a + 5" 和一个求值映射 {"e": 1}(以 evalvars = ["e"] 和 evalints = [1] 的形式给出),返回表示简化表达式的标记列表,如 ["-1*a","14"]。
表达式由块和符号交替组成,每个块和符号之间用空格分隔。
- 块可以是括号内的表达式、变量或非负整数。
- 变量是小写字母组成的字符串(不包含数字)。注意变量可以是多个字母,且变量从不带前导系数或一元运算符,如 “2x” 或 “-x”。
表达式按常规顺序求值:先括号,再乘法,然后加法和减法。
例如,表达式 "1 + 2 * 3" 的答案是 ["7"]。
输出格式如下:
- 对于每个自由变量系数非零的项,我们按字典序排列项内的自由变量。
- 例如,我们不会写 “bac”,只会写 “abc”。
- 项的次数等于相乘自由变量的个数(计算重数)。我们首先写出答案中次数最大的项,按字典序打破平局(忽略项的前导系数)。
- 例如,“aab*c” 的次数是 4。
- 项的前导系数直接放在左边,用星号与变量分隔(如果存在变量)。前导系数为 1 时仍要打印。
- 格式良好的答案示例是
["-2*a*a*a", "3*a*a*b", "3*b*b", "4*a", "5*c", "-6"]。 - 系数为 0 的项(包括常数项)不包含在内。
- 例如,表达式 “0” 的输出是
[]。
- 例如,表达式 “0” 的输出是
示例 1:
输入:expression = "e + 8 - a + 5", evalvars = ["e"], evalints = [1]
输出:["-1*a","14"]
示例 2:
输入:expression = "e - 8 + temperature - pressure", evalvars = ["e", "temperature"], evalints = [1, 12]
输出:["-1*pressure","5"]
示例 3:
输入:expression = "(e + 8) * (e - 8)", evalvars = [], evalints = []
输出:["1*e*e","-64"]
约束:
1 <= expression.length <= 250expression由小写英文字母、数字、’+’, ‘-’, ‘*’, ‘(’, ‘)’, ’ ’ 组成expression不包含前导或尾随空格expression中所有标记都由单个空格分隔0 <= evalvars.length <= 1001 <= evalvars[i].length <= 20evalvars[i]由小写英文字母组成evalints.length == evalvars.length-100 <= evalints[i] <= 100
解题思路
这是一个复杂的代数表达式计算问题,需要处理多项式运算。我们可以设计一个多项式类来表示和操作代数表达式。
核心思路:
多项式表示:使用映射结构,其中键是变量列表(按字典序排列),值是对应的系数。例如
"3*a*b"可表示为{["a","b"]: 3}。表达式解析:使用递归下降解析器或栈来处理运算符优先级和括号。按照数学运算规则:括号 > 乘法 > 加减法。
多项式运算:
- 加法:合并同类项,系数相加
- 减法:合并同类项,系数相减
- 乘法:每项与每项相乘,变量列表连接,系数相乘
变量替换:根据给定的变量映射,将已知变量替换为数值。
结果格式化:
- 按项的次数降序排列(次数相同则按变量字典序)
- 每项内变量按字典序排列
- 系数为0的项过滤掉
- 格式化为要求的字符串形式
实现步骤:
- 创建多项式类,支持基本运算
- 解析表达式为多项式
- 根据变量映射进行求值
- 将结果转换为指定格式
这种方法时间复杂度主要取决于表达式的复杂度和项的数量,空间复杂度为O(项数×变量数)。
代码实现
class Solution {
private:
struct Poly {
map<vector<string>, int> terms;
Poly() {}
Poly(int val) { if (val) terms[{}] = val; }
Poly(string var) { terms[{var}] = 1; }
Poly operator+(const Poly& other) const {
Poly result = *this;
for (auto& p : other.terms) {
result.terms[p.first] += p.second;
if (result.terms[p.first] == 0) {
result.terms.erase(p.first);
}
}
return result;
}
Poly operator-(const Poly& other) const {
Poly result = *this;
for (auto& p : other.terms) {
result.terms[p.first] -= p.second;
if (result.terms[p.first] == 0) {
result.terms.erase(p.first);
}
}
return result;
}
Poly operator*(const Poly& other) const {
Poly result;
for (auto& p1 : terms) {
for (auto& p2 : other.terms) {
vector<string> vars = p1.first;
vars.insert(vars.end(), p2.first.begin(), p2.first.end());
sort(vars.begin(), vars.end());
result.terms[vars] += p1.second * p2.second;
}
}
for (auto it = result.terms.begin(); it != result.terms.end();) {
if (it->second == 0) it = result.terms.erase(it);
else ++it;
}
return result;
}
void evaluate(unordered_map<string, int>& evalMap) {
map<vector<string>, int> newTerms;
for (auto& p : terms) {
vector<string> newVars;
int coeff = p.second;
for (string& var : p.first) {
if (evalMap.count(var)) {
coeff *= evalMap[var];
} else {
newVars.push_back(var);
}
}
if (coeff) newTerms[newVars] += coeff;
}
terms = newTerms;
}
};
vector<string> tokenize(string& expression) {
vector<string> tokens;
istringstream iss(expression);
string token;
while (iss >> token) tokens.push_back(token);
return tokens;
}
Poly parse(vector<string>& tokens, int& pos) {
return parseExpression(tokens, pos);
}
Poly parseExpression(vector<string>& tokens, int& pos) {
Poly result = parseTerm(tokens, pos);
while (pos < tokens.size() && (tokens[pos] == "+" || tokens[pos] == "-")) {
string op = tokens[pos++];
Poly term = parseTerm(tokens, pos);
if (op == "+") result = result + term;
else result = result - term;
}
return result;
}
Poly parseTerm(vector<string>& tokens, int& pos) {
Poly result = parseFactor(tokens, pos);
while (pos < tokens.size() && tokens[pos] == "*") {
pos++;
result = result * parseFactor(tokens, pos);
}
return result;
}
Poly parseFactor(vector<string>& tokens, int& pos) {
if (tokens[pos] == "(") {
pos++;
Poly result = parseExpression(tokens, pos);
pos++; // skip ")"
return result;
} else if (isdigit(tokens[pos][0])) {
return Poly(stoi(tokens[pos++]));
} else {
return Poly(tokens[pos++]);
}
}
public:
vector<string> basicCalculatorIV(string expression, vector<string>& evalvars, vector<int>& evalints) {
unordered_map<string, int> evalMap;
for (int i = 0; i < evalvars.size(); i++) {
evalMap[evalvars[i]] = evalints[i];
}
vector<string> tokens = tokenize(expression);
int pos = 0;
Poly poly = parse(tokens, pos);
poly.evaluate(evalMap);
vector<pair<vector<string>, int>> termsList;
for (auto& p : poly.terms) {
termsList.push_back(p);
}
sort(termsList.begin(), termsList.end(), [](const auto& a, const auto& b) {
if (a.first.size() != b.first.size())
return a.first.size() > b.first.size();
return a.first < b.first;
});
vector<string> result;
for (auto& term : termsList) {
string s = to_string(term.second);
if (!term.first.empty()) {
s += "*";
for (int i = 0; i < term.first.size(); i++) {
if (i > 0) s += "*";
s += term.first[i];
}
}
result.push_back(s);
}
return result;
}
};
class Solution:
def basicCalculatorIV(self, expression: str, evalvars: List[str], evalints: List[int]) -> List[str]:
class Poly:
def __init__(self, terms=None):
self.terms = terms if terms else {}
@classmethod
def from_int(cls, val):
poly = cls()
if val:
poly.terms[tuple()] = val
return poly
@classmethod
def from_var(cls, var):
poly = cls()
poly.terms[(var,)] = 1
return poly
def __add__(self, other):
result = Poly(dict(self.terms))
for key, val in other.terms.items():
result.terms[key] = result.terms.get(key, 0) + val
if result.terms[key] == 0:
del result.terms[key]
return result
def __sub__(self, other):
result = Poly(dict(self.terms))
for key, val in other.terms.items():
result.terms[key] = result.terms.get(key, 0) - val
if result.terms[key] == 0:
del result.terms[key]
return result
def __mul__(self, other):
result = Poly()
for key1, val1 in self.terms.items():
for key2, val2 in other.terms.items():
new_key = tuple(sorted(key1 + key2))
result.terms[new_key] = result.terms.get(new_key, 0) + val1 * val2
for key in list(result.terms.keys()):
if result.terms[key] == 0:
del result.terms[key]
return result
def evaluate(self, eval_map):
new_terms = {}
for key, coeff in self.terms.items():
new_key = []
for var in key:
if var in eval_map:
coeff *= eval_map[var]
else:
new_key.append(var)
if coeff:
new_key = tuple(sorted(new_key))
new_terms[new_key] = new_terms.get(new_key, 0) + coeff
self.terms = new_terms
def parse_expression(tokens, pos):
result = parse_term(tokens, pos)
while pos[0] < len(tokens) and tokens[pos[0]] in ['+', '-']:
op = tokens[pos[0]]
pos[0] += 1
term = parse_term(tokens, pos)
if op == '+':
result = result + term
else:
result = result - term
return result
def parse_term(tokens, pos):
result = parse_factor(tokens, pos)
while pos[0] < len(tokens) and tokens[pos[0]] == '*':
pos[0] += 1
result = result * parse_factor(tokens, pos)
return result
def parse_factor(tokens, pos):
if tokens[pos[0]] == '(':
pos[0] += 1
result = parse_expression(tokens, pos)
pos[0] += 1 # skip ')'
return result
elif tokens[pos[0]].isdigit():
val = int(tokens[pos[0]])
pos[0] += 1
return Poly.from_int(val)
else:
var = tokens[pos[0]]
pos[0] += 1
return Poly.from_var(var)
eval_map = dict(zip(evalvars, evalints))
tokens = expression.split()
pos = [0]
poly = parse_expression(tokens, pos)
poly.evaluate(eval_map)
# Sort terms by degree (descending) then lexicographically
terms_list = list(poly.terms.items())
terms_list.sort(key=lambda x: (-len(x[0]), x[0]))
result = []
for key, coeff in terms_list:
s = str(coeff)
if key:
s += '*' + '*'.join(key)
result.append(s)
return result
public class Solution {
public class Poly {
public Dictionary<List<string>, int> terms;
public Poly() {
terms = new Dictionary<List<string>, int>(new ListComparer());
}
public Poly(int val) : this() {
if (val != 0) terms[new List<string>()] = val;
}
public Poly(string var) : this() {
terms[new List<string> { var }] = 1;
}
public Poly Add(Poly other) {
var result = new Poly();
foreach (var kvp in terms) {
result.terms[new List<string>(kvp.Key)] = kvp.Value;
}
foreach (var kvp in other.terms) {
var key = new List<string>(kvp.Key);
if (result.terms.ContainsKey(key)) {
result.terms[key] += kvp.Value;
if (result.terms[key] == 0) result.terms.Remove(key);
} else {
result.terms[key] = kvp.Value;
}
}
return result;
}
public Poly Subtract(Poly other) {
var result = new Poly();
foreach (var kvp in terms) {
result.terms[new List<string>(kvp.Key)] = kvp.Value;
}
foreach (var kvp in other.terms) {
var key = new List<string>(kvp.Key);
if (result.terms.ContainsKey(key)) {
result.terms[key] -= kvp.Value;
if (result.terms[key] == 0) result.terms.Remove(key);
} else {
result.terms[key] = -kvp.Value;
}
}
return result;
}
public Poly Multiply(Poly other) {
var result = new Poly();
foreach (var kvp1 in terms) {
foreach (var kvp2 in other.terms) {
var newKey = new List<string>(kvp1.Key);
newKey.AddRange(kvp2.Key);
newKey.Sort();
if (result.terms.ContainsKey(newKey)) {
result.terms[newKey] += kvp1.Value * kvp2.Value;
} else {
result.terms[newKey] = kvp1.Value * kvp2.Value;
}
}
}
var keysToRemove = new List<List<string>>();
foreach (var kvp in result.terms) {
if (kvp.Value == 0) keysToRemove.Add(kvp.Key);
}
foreach (var key in keysToRemove) {
result.terms.Remove(key);
}
return result;
}
public void Evaluate(Dictionary<string, int> evalMap) {
var newTerms = new Dictionary<List<string>, int>(new ListComparer());
foreach (var kvp in terms) {
var newKey = new List<string>();
int coeff = kvp.Value;
foreach (var var in kvp.Key) {
if (evalMap.ContainsKey(var)) {
coeff *= evalMap[var];
} else {
newKey.Add(var);
}
}
if (coeff != 0) {
newKey.Sort();
if (newTerms.ContainsKey(newKey)) {
newTerms[newKey] += coeff;
} else {
newTerms[newKey] = coeff;
}
}
}
terms = newTerms;
}
}
public class ListComparer : IEqualityComparer<List<string>> {
public bool Equals(List<string> x, List<string> y) {
if (x.Count != y.Count) return false;
for (int i = 0; i < x.Count; i++) {
if (x[i] != y[i]) return false;
}
return true;
}
public int GetHashCode(List<string> obj) {
int hash = 17;
foreach (var item in obj) {
hash = hash * 31 + item.GetHashCode();
}
return hash;
}
}
private int pos;
public IList<string> BasicCalculatorIV(string expression, string[] evalvars, int[] evalints) {
var evalMap = new Dictionary<string, int>();
for (int i = 0; i < evalvars.Length; i++) {
evalMap[evalvars[i]] = evalints[i];
}
var tokens = expression.Split(' ');
pos = 0;
var poly = ParseExpression(tokens);
poly.Evaluate(evalMap);
var termsList = poly.terms.ToList();
termsList.Sort((a, b) => {
if (a.Key.Count != b.Key.Count) return b.Key.Count.CompareTo(a.Key.Count);
return string.Join("", a.Key).CompareTo(string.Join("", b.Key));
});
var result = new List<string>();
foreach (var term in termsList) {
var s = term.Value.ToString();
if (term.Key.Count > 0) {
s += "*" + string.Join("*", term.Key);
}
result.Add(s);
}
return result;
}
private Poly ParseExpression(string[] tokens) {
var result = ParseTerm(tokens);
while (pos < tokens.Length && (tokens[pos] == "+" || tokens[pos] == "-")) {
var op = tokens[pos++];
var term = ParseTerm(tokens);
if (op == "+") result = result.Add(term);
else result = result.Subtract(term);
}
return result;
}
private Poly ParseTerm(string[] tokens) {
var result = ParseFactor(tokens);
while (pos < tokens.Length && tokens[pos] == "*") {
pos++;
result = result.Multiply(ParseFactor(tokens));
}
return result;
}
private Poly ParseFactor(string[] tokens) {
if (tokens[pos] == "(") {
pos++;
var result = ParseExpression(tokens);
pos++; // skip ")"
return result;
} else if (char.IsDigit(tokens[pos][0])) {
return new Poly(int.Parse(tokens[pos++]));
} else {
return new Poly(tokens[pos++]);
}
}
}
var basicCalculatorIV = function(expression, evalvars, evalints) {
const evalMap = {};
for (let i = 0; i < evalvars.length; i++) {
evalMap[evalvars[i]] = evalints[i];
}
class Term {
constructor(coeff = 0, vars = []) {
this.coeff = coeff;
this.vars = vars.slice().sort();
}
multiply(other) {
return new Term(this.coeff * other.coeff, this.vars.concat(other.vars));
}
getKey() {
return this.vars.join('*');
}
getDegree() {
return this.vars.length;
}
}
class Polynomial {
constructor() {
this.terms = new Map();
}
addTerm(term) {
const key = term.getKey();
if (this.terms.has(key)) {
this.terms.get(key).coeff += term.coeff;
} else {
this.terms.set(key, new Term(term.coeff, term.vars));
}
}
add(other) {
const result = new Polynomial();
for (let term of this.terms.values()) {
result.addTerm(term);
}
for (let term of other.terms.values()) {
result.addTerm(term);
}
return result;
}
multiply(other) {
const result = new Polynomial();
for (let term1 of this.terms.values()) {
for (let term2 of other.terms.values()) {
result.addTerm(term1.multiply(term2));
}
}
return result;
}
toResult() {
const terms = Array.from(this.terms.values()).filter(term => term.coeff !== 0);
terms.sort((a, b) => {
if (a.getDegree() !== b.getDegree()) {
return b.getDegree() - a.getDegree();
}
return a.getKey().localeCompare(b.getKey());
});
return terms.map(term => {
if (term.vars.length === 0) {
return term.coeff.toString();
} else {
return term.coeff + '*' + term.vars.join('*');
}
});
}
}
function parseTerm(token) {
if (/^\d+$/.test(token)) {
const poly = new Polynomial();
poly.addTerm(new Term(parseInt(token), []));
return poly;
} else if (evalMap.hasOwnProperty(token)) {
const poly = new Polynomial();
poly.addTerm(new Term(evalMap[token], []));
return poly;
} else {
const poly = new Polynomial();
poly.addTerm(new Term(1, [token]));
return poly;
}
}
function parseExpression(tokens, index) {
let result = parseFactor(tokens, index);
while (index.value < tokens.length && (tokens[index.value] === '+' || tokens[index.value] === '-')) {
const op = tokens[index.value];
index.value++;
const right = parseFactor(tokens, index);
if (op === '+') {
result = result.add(right);
} else {
const negRight = new Polynomial();
for (let term of right.terms.values()) {
negRight.addTerm(new Term(-term.coeff, term.vars));
}
result = result.add(negRight);
}
}
return result;
}
function parseFactor(tokens, index) {
let result = parseAtom(tokens, index);
while (index.value < tokens.length && tokens[index.value] === '*') {
index.value++;
const right = parseAtom(tokens, index);
result = result.multiply(right);
}
return result;
}
function parseAtom(tokens, index) {
if (tokens[index.value] === '(') {
index.value++;
const result = parseExpression(tokens, index);
index.value++;
return result;
} else {
const result = parseTerm(tokens[index.value]);
index.value++;
return result;
}
}
const tokens = expression.split(' ');
const index = { value: 0 };
const result = parseExpression(tokens, index);
return result.toResult();
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(N × M × K) | N为表达式长度,M为项数,K为平均变量数 |
| 空间复杂度 | O(M × K) | M为多项式项数,K为每项的平均变量数 |
相关题目
. Parse Lisp Expression (Hard)
. Basic Calculator III (Hard)