题目地址:
https://leetcode.com/problems/verbal-arithmetic-puzzle/
给定一个单词列表 A A A,和一个单词 s s s,题目保证只含英文大写字母,并且 l A ≤ 5 l_A\le 5 lA≤5。问是否能构造一个大写字母到 0 ∼ 9 0\sim 9 0∼9的映射,使得将所有单词看成数字的时候, A A A求和恰好等于 s s s。映射不可使得单词含前导 0 0 0。
直接暴搜即可。枚举每个字母被映射成哪个数字,同时排除掉非法情形(包括前导 0 0 0)。代码如下:
class Solution {
public:
bool used[10];
int mp[26];
bool isSolvable(vector<string>& ws, string s) {
for (auto& w : ws) reverse(w.begin(), w.end());
reverse(s.begin(), s.end());
unordered_set<char> set;
int max_len = 0;
for (auto& w : ws) {
if (w.size() > s.size()) return false;
max_len = max(max_len, (int)w.size());
for (auto c : w) set.insert(c);
}
for (auto c : s) set.insert(c);
// 由于ws长度小于等于5,s的长度不能比ws的最长长度长超过1
if (set.size() > 10 || max_len + 1 < s.size()) return false;
memset(used, 0, sizeof used);
memset(mp, -1, sizeof mp);
return dfs(0, 0, 0, s, ws);
}
// sum是计算到第k位的时候当前取得的和是多少,widx是当前要从第几个单词开始枚举
bool dfs(int k, int widx, int sum, string& s, vector<string>& ws) {
if (k == s.size()) return !sum;
for (int i = widx; i < ws.size(); i++) {
auto& w = ws[i];
if (k >= w.size()) continue;
if (!~mp[w[k] - 'A']) {
// 枚举w[k]可以映射为哪个数
for (int j = 0; j < 10; j++) {
// 前导0是非法的
if (!j && k == w.size() - 1 && w.size() > 1) continue;
if (!used[j]) {
used[j] = true;
mp[w[k] - 'A'] = j;
sum += j;
if (dfs(k, i + 1, sum, s, ws)) return true;
used[j] = false;
mp[w[k] - 'A'] = -1;
sum -= j;
}
}
// 如果w[k]无论映射为哪个数都不行,则无解
return false;
// 如果w[k]是前导0,也无解
} else if (!mp[w[k] - 'A'] && k == w.size() - 1 && w.size() > 1)
return false;
else
sum += mp[w[k] - 'A'];
}
int t = sum % 10;
// 如果s[k]是前导0,无解
if (k == s.size() - 1 && s.size() > 1 && (!mp[s[k] - 'A'] || !t)) return false;
if (!~mp[s[k] - 'A'] && !used[t]) {
mp[s[k] - 'A'] = t;
used[t] = true;
if (dfs(k + 1, 0, sum / 10, s, ws)) return true;
mp[s[k] - 'A'] = -1;
used[t] = false;
} else if (mp[s[k] - 'A'] == t)
return dfs(k + 1, 0, sum / 10, s, ws);
return false;
}
};
时间复杂度指数级,空间 n n n, n n n为字母总数。