Leetcode week, which babies can understand – 171 – 4

Time:2020-10-14

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

Leetcode week, which babies can understand - 171 - 4

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 is|x1 – 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
  • eachword[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 title|x1 - 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 isdistance(a, a)That is to say0
  • If there are two characters, then our overhead isdistance(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 arexandyIn whichx < ySo for us, from the position0From the beginning to the endyThis string, when placed on a finger in thexThe optimal solution is calleddp[x][y]
  • One finger from positionxMove toyIs calleddistance(x, y)
  • One finger from position0Move to location1And continue to move to positionxThe 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

  • aboutdp[3][6]Its source can only bedp[3][5] + distance(5, 6)
  • aboutdp[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 so-called 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:

  1. initializationdpArray andsumArray.
  2. According to the recurrence formula, it is deduced thatdpThe value we need in the array.
  3. 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 putdpThis two-dimensional array is optimized to a one-dimensional 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,yRepresents 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. aboutsumArray. 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 top-down and bottom-up. And the recursive content and specific logic are also set by usdpArray. 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

Leetcode week, which babies can understand - 171 - 4

Recommended Today

Summary of recent use of gin

Recently, a new project is developed by using gin. Some problems are encountered in the process. To sum up, as a note, I hope it can help you. Cross domain problems Middleware: func Cors() gin.HandlerFunc { return func(c *gin.Context) { //Here you can use * or the domain name you specify c.Header(“Access-Control-Allow-Origin”, “*”) //Allow header […]