Algorithm (Recursion)
- Let the $f(i,V,\texttt{acc})$ denote the maximum score that could be achieved when dealing with
word[i]
and with acc
scores already pocketed and $V$ available letters.
- Then we have $$f(i,V,\mathrm{acc})=\begin{cases}
\max(f(i+1,V,\mathrm{acc}),f(i+1,V-\{\text{word}[i]\},\text{acc}+\text{score}(\text{word}[i])) & \text{if }\text{word}[i]\in V\\\\
f(i+1,V,\text{acc}) & \text{if word}[i]\notin V\\\\
\mathrm{acc} & \text{if }i\geq N,
\end{cases}.$$ where $N$ is the size of the
word
list.
- We implements this in a more costly but persistent manner. To speed up one could use a traditional backtracking approach.
Code
template <class F>
struct recursive {
F f;
template <class... Ts>
decltype(auto) operator()(Ts&&... ts) const { return f(std::ref(*this), std::forward<Ts>(ts)...); }
template <class... Ts>
decltype(auto) operator()(Ts&&... ts) { return f(std::ref(*this), std::forward<Ts>(ts)...); }
};
template <class F> recursive(F) -> recursive<F>;
auto const rec = [](auto f){ return recursive{std::move(f)}; };
class Solution {
public:
int maxScoreWords(vector<string>& words, vector<char>& letters, vector<int>& score) {
const auto N = size(words);
struct config_t {
const unordered_map<char, int> data;
config_t consume(const string & word) {
const auto new_data = [&](unordered_map<char, int> self = {}) {
std::copy(begin(data), end(data), std::inserter(self, begin(self)));
for (const auto ch : word)
self[ch]--;
return self;
}();
return {new_data};
}
bool contains(const string& str, bool acc = true) {
const auto local_counter = [&](unordered_map<char, int> self = {}) {
for (const auto x : str)
self[x]++;
return self;
}();
for (int i = 0; i < size(str) and acc == true; ++i)
acc = (acc and (data.count(str[i]) and data.at(str[i]) >= local_counter.at(str[i])));
return acc;
}
};
const auto initial_config = [&](unordered_map<char, int> data = {}) {
for (const auto ch : letters)
data[ch]++;
return config_t{data};
}();
auto get_score = [&](const string & str, int acc = 0) {
for (const auto ch : str)
acc += score[ch - 'a'];
return acc;
};
auto f = rec([&](auto&& f, int i, int acc, config_t config) -> int {
if (i == N)
return acc;
else if (config.contains(words[i]))
return std::max(f(i + 1, acc + get_score(words[i]), config.consume(words[i])), f(i + 1, acc, config));
else
return f(i + 1, acc, config);
});
return f(0, 0, initial_config);
}
};
Loved the explanation!
I modified the solution to be more efficient…