Mô tả bài toán
Cho biểu thức chuỗi chứa số và toán tử +, -, *. Tìm tất cả kết quả có thể được từ các cách đặt ngoặc khác nhau.
Ví dụ: "2*3-4*5" → [-34, -14, -10, -10, 10] (5 cách đặt ngoặc).
Giải thuật Chia để trị (Divide & Conquer) + Memoization
- Duyệt chuỗi, tại mỗi toán tử
optại vị tríi:- Đệ quy tính tất cả kết quả của phần trái
s[0..i-1]. - Đệ quy tính tất cả kết quả của phần phải
s[i+1..end]. - Kết hợp: với mọi
xtrong trái,ytrong phải, tínhx op y.
- Đệ quy tính tất cả kết quả của phần trái
- Nếu chuỗi không có toán tử → chuyển thành số, trả về mảng 1 phần tử.
- Dùng memoization (map
string → vector) tránh tính lại.
Cài đặt C++
#include <bits/stdc++.h>
using namespace std;
void print_vec(vector<int> v) {
cout << "[";
for (int x : v) cout << x << ", ";
cout << "]" << endl;
}
class Solution {
unordered_map<string, vector<int>> memo;
public:
vector<int> diffWaysToCompute(string input) {
if (memo.count(input)) return memo[input];
vector<int> res;
for (int i = 0; i < input.size(); i++) {
char c = input[i];
if (c == '+' || c == '-' || c == '*') {
vector<int> left = diffWaysToCompute(input.substr(0, i));
vector<int> right = diffWaysToCompute(input.substr(i + 1));
for (int x : left) {
for (int y : right) {
if (c == '+') res.push_back(x + y);
else if (c == '-') res.push_back(x - y);
else res.push_back(x * y);
}
}
}
}
if (res.empty()) res.push_back(stoi(input));
return memo[input] = res;
}
};
int main() {
Solution ob;
print_vec(ob.diffWaysToCompute("2*3-4*5")); // [-34, -10, -14, -10, 10]
return 0;
}
Độ phức tạp
- Thời gian: Số Catalan (số cách đặt ngoặc) × độ dài biểu thức. Với memo:
O(n^3)xấp xỉ. - Không gian:
O(n^2)cho memo.