Leetcode 1320. Minimum Distance to Type a Word Using Two Fingers
Problem Explanation
The problem requires us to find the minimum distance to type a string using two fingers on a given keyboard. The keyboard layout is provided in the XY plane, where each English uppercase letter is located at some coordinate. For example, the letter 'A' is located at coordinate (0,0), 'B' at coordinate (0,1), 'P' at coordinate (2,3), and 'Z' at coordinate (4,1).
The distance between coordinates (x1, y1) and (x2, y2) is |x1 - x2| + |y1 - y2|. We must return the minimum total distance to type the string word using only two fingers. Note that we don't count the initial positions of fingers towards the total distance. Also, the fingers don't need to start at the first letter or the first two letters.
The approach used in the provided solution is dynamic programming, where dp[i][j][k] stores the minimum distance with the left finger on i-th char and the right finger on j-th char, and already written the k first words. The algorithm compares the minimum distances when moving left or right finger to type the next character and choose the smaller one.
Walkthrough an Example
The solution involves hovering both fingers at a specific location before beginning.
Let's take an example where word = "CAKE"
- Initially, both fingers are in a hovering mode to any location. So, the cost is 0.
- Place the 1st finger on 'C'. The distance will again be 0, because the finger was initially hovering.
- Move the 1st finger to 'A'. The distance travelled will be |x1 - x2| + |y1 - y2| (distance from 'C' to 'A' considering their coordinates)
- Repeat steps 2-3 for the other characters.
The total distance will be the sum of distances travelled.
Python Solution
1 2python 3class Solution: 4 def minimumDistance(self, word: str) -> int: 5 dp = [[0]*27 for _ in range(27)] 6 costSoFar = [0]*(len(word)+1) 7 for i in range(len(word)-1, -1, -1): 8 costSoFar[i] = costSoFar[i+1] + self.dist(word[i], word[i+1]) if i+1 < len(word) else 0 9 for i in range(len(word)-1, -1, -1): 10 for j in range(27): 11 dp[j][ord(word[i])-65] = min(dp[k][j]+self.dist(word[i], chr(k+65))-costSoFar[i+1]+costSoFar[i] for k in range(27)) 12 return min(dp) 13 14 15 def dist(self, a: str, b: str) -> int: 16 if a is None: return 0 17 return abs((ord(a)-65)//6 - (ord(b)-65)//6) + abs((ord(a)-65)%6 - (ord(b)-65)%6)
Java Solution
1 2java 3class Solution { 4 public int minimumDistance(String word) { 5 int[][] dp = new int[27][27]; 6 int[] costSoFar = new int[word.length() + 1]; 7 for (int i = word.length() - 1; i >= 0; --i) 8 costSoFar[i] = costSoFar[i + 1] + dist(word.charAt(i), word.charAt(i + 1)); 9 for (int i = word.length() - 2; i >= 0; --i) 10 for (int j = 0; j < 27; ++j) 11 dp[j][word.charAt(i) - 'A'] = Math.min(dp[j][word.charAt(i + 1) - 'A'], Arrays.stream(dp).map(ar -> ar[word.charAt(i + 1) - 'A']).min().getAsInt() + dist(word.charAt(i), (char) (j + 'A')) - costSoFar[i + 2] + costSoFar[i + 1]); 12 return Arrays.stream(dp).map(ar -> ar[word.charAt(0) - 'A']).min().getAsInt(); 13 } 14 15 private int dist(char a, char b) { 16 return Math.abs(a / 6 - b / 6) + Math.abs(a % 6 - b % 6); 17 } 18}
JavaScript Solution
1 2javascript 3/** 4 * @param {string} word 5 * @return {number} 6 */ 7var minimumDistance = function(word) { 8 let n = word.length; 9 let dp = Array.from(Array(27), () => Array.from(Array(n + 1), () => 0)); 10 for(let i = n-1; i >= 0; i--) { 11 for(let left = 26; left >= 0; left--) { 12 for(let right = 26; right >= 0; right--) { 13 let nl = dist(left, word.charCodeAt(i) - 'A'.charCodeAt(0)) + dp[right][i + 1]; 14 let nr = dist(right, word.charCodeAt(i) - 'A'.charCodeAt(0)) + dp[left][i + 1]; 15 dp[left][i] = Math.min(nl, nr); 16 } 17 } 18 } 19 return dp[26][0]; 20}; 21 22var dist = function(a, b) { 23 if(a == 26) return 0; 24 return Math.abs(Math.floor(a / 6) - Math.floor(b / 6)) + Math.abs(a % 6 - b % 6); 25};
C++ Solution
1
2cpp
3class Solution {
4public:
5 int minimumDistance(string word) {
6 vector<vector<int>> dp(27, vector<int>(27, 0));
7 vector<int> costSoFar(word.size() + 1, 0);
8 for (int i = word.size() - 1; i > 0; --i)
9 costSoFar[i] = costSoFar[i + 1] + dist(word[i], word[i-1]);
10 for (int i = word.size() - 2; i >= 0; --i)
11 for (int j = 0; j < 27; ++j)
12 dp[j][word[i] - 'A'] = max({dp[j][word[i] - 'A'], costSoFar[i+2] - costSoFar[word.size()] + dp[word[i+1] - 'A'][j]});
13 return costSoFar[0] - *max_element(dp[0].begin(), dp[0].end());
14 }
15
16private:
17 int dist(char a, char b) {
18 return abs(a / 6 - b / 6) + abs(a % 6 - b % 6);
19 }
20};
CSharp Solution
1 2csharp 3public class Solution { 4 public int MinimumDistance(string word) { 5 int[][] dp = new int[27][]; 6 for(int i = 0; i < 27; ++i) 7 dp[i] = new int[27]; 8 int n = word.Length; 9 int[] cost = new int[n + 1]; 10 for(int i = n - 1; i > 0; --i) 11 cost[i] = cost[i + 1] + dist(word[i], word[i - 1]); 12 for(int i = n - 2; i >= 0; --i) 13 for(int j = 0; j < 27; ++j) 14 dp[j][word[i] - 'A'] = Math.Max(dp[j][word[i] - 'A'], cost[i + 2] - cost[n] + dp[word[i+1] - 'A'][j]); 15 return cost[0] - dp.Max(x => x.Max()); 16 } 17 private int dist(char a, char b){ 18 int ia = a == ' ' ? 26 : a - 'A', ib = b == ' '? 26: b - 'A'; 19 return Math.Abs(ia / 6 - ib / 6) + Math.Abs(ia % 6 - ib % 6); 20 } 21}
It's important to note that the C++, Java and CSharp solution are using max values instead of min values due to negative indexing not being available in these languages.These solutions try to find the minimum total distance to type a string using two fingers, by availing dynamic programming approach to break down the problem into simpler sub-problems.
It's important to implement rigorous testing, particularly edge cases. For instance, if the typed string or word is null or it only contains one letter, the minimum distance will be 0 because you don't need to move your fingers.
If the word contains a lot of same characters in consecutive positions(for example 'AAAAAAAB'), the minimum distance will also be 0, because you just need to put one of your fingers on the letter 'A' and keep typing it, without having to move your fingers.
These solutions are efficient as they use a bottom-up approach to compute the minimum distance, avoiding re-computation of overlapping sub-problems. However, they may be a bit hard to follow due to multiple nested loops and complex conditional checking. You may make them feel a bit more digestible by adding additional comments explaining the logic.
The time complexity for these solutions is O(n) where n is the length of the word as we're traversing the word only once. However, bear in mind that in worst-case scenario where all characters in the word are distinct, the space complexity could be relatively high, as it uses a 2D matrix for storing the minimum distance which could go up to O(nยฒ).
These solutions work for words with any case (lowercase / uppercase) as they convert word characters into uppercase before operating. However, they will break for non-ASCII characters or special characters as they're not handled in these solutions.
Overall, these solutions are a great learning resource for understanding dynamic programming and applying it to real-world problems. They also provide a practical way of thinking about problem solving by breaking down complex problems into smaller, simpler sub-problems. For those who're working on dynamic programming problems, these solutions provide a solid foundation for understanding how to approach such problems, regardless of programming language.
Got a question?ย Ask the Teaching Assistantย anything you don't understand.
Still not clear? Ask in the Forum, ย Discordย orย Submitย the part you don't understand to our editors.