1320. Minimum distance of two finger input
Hi, everyone. I’m a pig. Welcome to the weekly leetcode quiz of the “baby can understand” series.
This is the fourth question in issue 171 and 1320 in the list of questions — “the minimum distance of two points input”
Title Description
The layout of two finger input customized keyboard on the XY plane is shown in the above figure, in which each capital English letter is located at a certain coordinate, such as a letterAAt coordinates(0,0), lettersBAt coordinates(0,1), lettersPAt coordinates(2,3)And letterZAt coordinates(4,1)。
Give you a string to be typedword
, please calculate and return the minimum total move distance required to type the string with only two fingers. Coordinates(x1,y1)and(x2,y2)The distance between them isx1 – x2 + y1 – y2。
Note that the starting position of the two fingers is zero cost and does not include the total moving distance. You don’t have to start with the first letter or the first two letters.
Example 1:
Input: word = "cake"
Output: 3
Explanation:
One of the best ways to input "cake" with two fingers is:
Finger 1 on letter 'C'   move distance = 0
Finger 1 on letter 'a' 'moving distance = distance from letter 'C' to letter 'a' = 2
Finger 2 on the letter 'k'   move distance = 0
Finger 2 on the letter 'e'   moving distance = distance from letter 'k' to letter 'e' = 1
Total distance = 3
Example 2:
Input: word = "happy"
Output: 6
Explanation:
One of the best ways to enter "happy" with two fingers is:
Finger 1 on letter 'H'   move distance = 0
Finger 1 on letter 'a' 'moving distance = distance from letter 'H' to letter 'a' = 2
Finger 2 on the letter 'p'   move distance = 0
Finger 2 on the letter 'p'   moving distance = distance from letter 'p' to letter 'p' = 0
Finger 1 on the letter 'y'   moving distance = distance from letter 'a' to letter 'y' = 4
Total distance = 6
Example 3:
Input: word = "new"
Output: 3
Example 4:
Input: word = "year"
Output: 7
Tips:
2 <= word.length <= 300
 each
word[i]
It’s all capital letters.
Official difficulty
HARD
Solutions
The content of the title is very simple, is to use two fingers, input a string of given string, request to return the shortest distance of finger movement. One of the keyboard layout has been given through the pictures.
After reading the topic, the pig’s hoof kicks and the tail cocks, because the description of a simple topic may be really simple, or it may be complicated to think about. The difficulty of this problem is hardIt’s time to have a few more chicken dishes to relaxIt’s time to be serious<
Let’s not think about the logic of the topic, but just look at the requirements. The first obvious thing is that we need to know the cost of moving fingers from one letter to another on the keyboard. Let’s implement this helper method first.
Because English letters are arranged in order, we can easily think of char code to do calculation. The formula of moving distance given by the titlex1  x2 + y1  y2
It’s natural for us to think about it. We can decompose the distance into two directions: horizontal and vertical, and finally sum it. Since the number of letters in each line is fixed, we can find the number of lines where the two letters are located for the vertical distance, and then subtract them. For horizontal distance, because the number of characters in each line is fixed, we can directly carry out modular operation and then subtract.
One thing to note here is not to forget to take absolute value after subtraction. The specific code may be similar to the following:
function distance(a, b) {
const x = word.charCodeAt(a)  65;
const y = word.charCodeAt(b)  65;
return Math.abs((x % 6)  (y % 6)) + Math.abs(((x / 6) << 0)  ((y / 6) << 0));
}
Then it comes to the core problem, how to determine the logic of the solution. Here, if you feel that you can’t do anything for a moment, you can give a few examples
 If there is one character, then our overhead is
distance(a, a)
That is to say0
。  If there are two characters, then our overhead is
distance(a, a) + distance(b, b)
That’s right0
。 
If there are three characters, our overhead may be as follows:

distance(a, a) + distance(b, b) + distance(b, c)
。 
distance(a, a) + distance(b, b) + distance(a, c)
。 
distance(a, a) + distance(a, b) + distance(c, c)
。


If there are four characters, our overhead may be as follows:

distance(a, a) + distance(b, b) + distance(b, c) + distance(c, d)
。 
distance(a, a) + distance(b, b) + distance(b, c) + distance(a, d)
。 
distance(a, a) + distance(b, b) + distance(a, c) + distance(c, d)
。 
distance(a, a) + distance(b, b) + distance(a, c) + distance(b, d)
。 
distance(a, a) + distance(a, b) + distance(c, c) + distance(c, d)
。 
distance(a, a) + distance(a, b) + distance(c, c) + distance(b, d)
。 
distance(a, a) + distance(a, b) + distance(b, c) + distance(d, d)
。

 If there are five characters… Wait a minute, do you want to kill the pig, although the pork is expensive
Friends can take a closer look at the above examples. I wrote the order deliberately, just to show the information more easily. Here are some of the things we can find out:
 The first message is that our fingers must start with the first character. (ah, don’t look white
 The second message is that one of our fingers must end up at the end. (ah, don’t hit me

The third message is, if we’ve already used two fingers (hmm? How strange…), then the optimal solution of the next step may have two situations:
 Move the first finger to the end.
 Move the second finger to the end.
 The fourth message is that if we have not used the second finger, then the optimal solution to continue to the next step must be to use the second finger.
The above four information may seem very trivial, but this is the basis of our subsequent derivation. Let’s move on to the formal logic.
dynamic programming
We have got some basic information about the fingers between the steps above, so we may have a little partner put forward such an idea at this time. We put the first finger directly at the beginning and the second finger at the farthest point. Then each time the character moves, it moves with the nearest finger to the target. Is this the solution? With this assumption, we can try the examples given in the title, such as “cake”, “happy”, and we will find that we can get the correct results. So it seems that the problem is very simple.
Don’t worry, let’s take another example of “zknbz”. According to the above idea, we will put the finger on “Z” and the farthest “K”, and then according to the principle of being closest to the target, we will continue to move “Z” = > “n”, “n” = > “B”, “B” = > “Z”. The total distance of the final movement is 8. However, let’s look back at this scheme, still put your fingers on “Z” and “K”, and then move “K” = > “n”, “n” = > b “,” Z “= >” Z “. The final total movement distance is 6. This proves that the assumption in front of us is problematic.
So what’s the problem? Two concepts are introduced here, local optimal solution and global optimal solution. The former refers to the optimal solution we get under the current situation. For example, each movement in the above assumption uses the nearest finger, so that we can get the optimal solution in the current step. The latter refers to the optimal solution we get after the end of the whole process. For example, in the above example, we finally get the solution of 6. Obviously, we get the local optimal solution, but we don’t get the global optimal solution.
I believe that some of the partners have already reflected. The above assumption is not the implementation of a greedy algorithm, that is, trying to use the local optimal solution to finally derive the global optimal solution. But this is conditional, for our problem, the local optimal solution can not derive the global optimal solution. Therefore, we should try to derive the global optimal solution based on the relationship between the steps by using the dynamic programming method.
So let’s look at the relationship between the steps. Here we will explain the meaning of the following data:
 Suppose the current positions of the two fingers are
x
andy
In whichx < y
So for us, from the position0
From the beginning to the endy
This string, when placed on a finger in thex
The optimal solution is calleddp[x][y]
。  One finger from position
x
Move toy
Is calleddistance(x, y)
。  One finger from position
0
Move to location1
And continue to move to positionx
The sum of the distances required is calledsum(x)
。
So let’s take a lookdp[x][y]
We can give a few specific examples to facilitate our thinking
 about
dp[3][6]
Its source can only bedp[3][5] + distance(5, 6)
。  about
dp[5][6]
It may come fromdp[0][5] + distance(0, 6)
Or it could bedp[1][5] + distance(1, 6)
Untildp[4][5] + distance(4, 6)
And don’t forget it could besum(5) + distance(6, 6)
。
Have you noticed the significance of the first few examples and the information we got. As the socalled old saying goes: important information, do not need money 4<
So we can abstract the above example intodp[x][y]
We can get the following calculation method:
dp[x][y] = x !== y  1
? dp[x][y  1] + distance(y  1, y)
: Math.min(sum(x), dp[0][x] + distance(0, y), dp[1][x] + distance(1, y), ..., dp[x  1][x] + distance(x  1, y))
At this time, we will see what the result of the title requires. If we use the above values to express it, it is actuallyMath.min(sum(n  1), dp[0][n  1], dp[1][n  1], ..., dp[n  2][n  1])
。
Now that we have the recursive formula and the value of the final result, we only need to implement the calculation in code. The specific process is as follows:
 initialization
dp
Array andsum
Array.  According to the recurrence formula, it is deduced that
dp
The value we need in the array.  According to the value of the result, the result is calculated.
Based on this process, we can implement code like this:
const minimumDistance = word => {
const LEN = word.length;
const dp = new Array(LEN  1);
const sum = new Uint16Array(LEN);
for (let i = 1; i < LEN; ++i) {
dp[i  1] = new Uint16Array(LEN);
sum[i] += sum[i  1] + distance(i  1, i);
}
for (let j = 2; j < LEN; ++j) {
let min = sum[j  1];
for (let i = 0; i < j  1; ++i) {
const min2 = dp[i][j  1] + distance(i, j);
if (min2 < min) min = min2;
dp[i][j] = dp[i][j  1] + distance(j  1, j);
}
dp[j  1][j] = min;
}
let min = sum[LEN  1];
for (let i = 0; i < LEN  1; ++i) {
if (dp[i][LEN  1] < min) min = dp[i][LEN  1];
}
return min;
function distance(a, b) {
const x = word.charCodeAt(a)  65;
const y = word.charCodeAt(b)  65;
return Math.abs((x % 6)  (y % 6)) + Math.abs(((x / 6) << 0)  ((y / 6) << 0));
}
};
optimization
According to the Convention, we still try to putdp
This twodimensional array is optimized to a onedimensional array. It’s a lot of trouble to make complaints about the multidimensional array in JS.
The optimization here is very simpledp[x][y]
Medium,y
Represents the current position of the right finger. This position is actually the character of the current deduction. And this deduction is actually carried out round by round, which is directly bound to our cycle. And we don’t need any earlier derived values in our subsequent calculations. Speaking of this, I believe the partners also found that we do not need the value of this dimension at all, because we only need to record the latest value.
Naturally, we can get the following code:
const minimumDistance = word => {
const LEN = word.length;
const dp = new Uint16Array(LEN  1);
const sum = new Uint16Array(LEN);
for (let i = 1; i < LEN; ++i) {
sum[i] += sum[i  1] + distance(i  1, i);
}
for (let j = 2; j < LEN; ++j) {
let min = sum[j  1];
for (let i = 0; i < j  1; ++i) {
const min2 = dp[i] + distance(i, j);
dp[i] = dp[i] + distance(j  1, j);
if (min2 < min) min = min2;
}
dp[j  1] = min;
}
return Math.min(...dp, sum[LEN  1]);
function distance(a, b) {
const x = word.charCodeAt(a)  65;
const y = word.charCodeAt(b)  65;
return Math.abs((x % 6)  (y % 6)) + Math.abs(((x / 6) << 0)  ((y / 6) << 0));
}
};
This code has run 56ms, temporarily beats 100%.
Re optimization
Looking back at the code above, we can see that some of the overhead can still be cut off. aboutsum
Array. If you look at the logic carefully, you will find that we only need its latest value. So there’s no need to continue to maintain this array. The code after removing this part is as follows:
const minimumDistance = word => {
const dp = new Uint16Array(word.length  1);
let sum = 0;
let step = distance(0, 1);
for (let j = 2; j < word.length; ++j) {
sum += step;
step = distance(j  1, j);
let min = sum;
for (let i = 0; i < j  1; ++i) {
const min2 = dp[i] + distance(i, j);
dp[i] = dp[i] + step;
if (min2 < min) min = min2;
}
dp[j  1] = min;
}
return Math.min(...dp, sum);
function distance(a, b) {
const x = word.charCodeAt(a)  65;
const y = word.charCodeAt(b)  65;
return Math.abs((x % 6)  (y % 6)) + Math.abs(((x / 6) << 0)  ((y / 6) << 0));
}
};
summary
In the above analysis process, we mentioned local optimal solution, global optimal solution, greedy algorithm and dynamic programming. However, here we did not do a detailed expansion, just point to the end. When we wait for the specific topic content in the future, we will talk about it in detail. If you are looking forward to it, you will be more eager. If you urge more pigs, you will have more power to explode liver duck<
In addition, there are many kinds of dynamic planning ideas, such as topdown and bottomup. And the recursive content and specific logic are also set by usdp
Array. You are welcome to actively supplement other programs. Yo~ Put your hands up~
Related links
 Weekly contest 171 topic list
 Repo
 My segment fault column
 My Zhihu column