Algorithm (Dynamic Programming)
- Let $f(i,j,k)$ the minimum distance to have filled
word[1:i]
when left and right hand finish at position $(i,j).$
- Then we have $$f(i,j,k)=\begin{cases}
0 & \text{if }i=0\text{ and }(i=\text{word}[i]\text{ or }j=\text{word}[i])\\\\
\min_{\mathrm{prev}\in\{A,…,Z\}}\{f(i-1,\mathrm{prev},k)+D[\mathrm{prev}][\mathrm{word}[i]]\} & \text{else if }i=\mathrm{word}[i]\\\\
\min_{\mathrm{prev}\in\{A,…,Z\}}\{f(i-1,i,\mathrm{prev})+D[\mathrm{prev}][\mathrm{word}[i]]\} & \text{else if }j=\text{word}[i]\\\\
\infty & \text{otherwise}
\end{cases}.$$
- This can be implemented using traditional dynamic programming technique.
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 minimumDistance(string word) {
const struct {
mutable optional<int> f[300][26][26] = {};
} memo;
const auto keys = [&](vector<pair<int, int>> self = {}) {
for (int i = 0; i < 26; ++i)
self.emplace_back(i / 6 , i % 6);
return self;
}();
const auto D = [&](vector<vector<int>> self = {}) {
self.assign(26, vector<int>(26));
for (int i = 0; i < 26; ++i)
for (int j = 0; j < 26; ++j) {
const auto [x1, y1] = keys[i];
const auto [x2, y2] = keys[j];
self[i][j] = std::abs(x1 - x2) + std::abs(y1 - y2);
}
return self;
}();
auto f = rec([&, memo = std::ref(memo.f)](auto&&f, int k, int i, int j) -> int {
if (memo[k][i][j])
return *memo[k][i][j];
else
return *(memo[k][i][j] = [&] {
const auto ch = word[k] - 'A';
if (k == 0 and (i == ch or j == ch))
return 0;
else if (i == ch)
return [&](int acc = INT_MAX) {
for (int prev = 0; prev < 26; ++prev)
acc = std::min(acc, f(k - 1, prev, j) + D[prev][ch]);
return acc;
}();
else if (j == ch)
return [&](int acc = INT_MAX) {
for (int prev = 0; prev < 26; ++prev)
acc = std::min(acc, f(k - 1, i, prev) + D[prev][ch]);
return acc;
}();
else if (i != ch and j != ch)
return INT_MAX / 2;
else
throw std::domain_error("unsupported case");
}());
});
const auto solution = [&](int acc = INT_MAX) {
const auto n = size(word) - 1;
const auto ch = word[n] - 'A';
for (int i = 0; i < 26; ++i)
acc = std::min({acc, f(n, i, ch), f(n, ch, i)});
return acc;
}();
return solution;
}
};