LeetCode 821. Shortest Distance to a Character

Given a string s and a character c that occurs in s, return an array of integers answer where answer.length == s.length and answer[i] is the distance from index i to the closest occurrence of character c in s.

The distance between two indices i and j is abs(i - j), where abs is the absolute value function.

Example 1:

Input: s = "loveleetcode", c = "e"
Output: [3,2,1,0,1,0,0,1,2,2,1,0]
Explanation: The character 'e' appears at indices 3, 5, 6, and 11 (0-indexed).
The closest occurrence of 'e' for index 0 is at index 3, so the distance is abs(0 - 3) = 3.
The closest occurrence of 'e' for index 1 is at index 3, so the distance is abs(1 - 3) = 2.
For index 4, there is a tie between the 'e' at index 3 and the 'e' at index 5, but the distance is still the same: abs(4 - 3) == abs(4 - 5) = 1.
The closest occurrence of 'e' for index 8 is at index 6, so the distance is abs(8 - 6) = 2.

Example 2:

Input: s = "aaab", c = "b"
Output: [3,2,1,0]

Constraints:

  • 1 <= s.length <= 104
  • s[i] and c are lowercase English letters.
  • It is guaranteed that c occurs at least once in s.

This question gives a string and a char, and requires the distance between each character in the string and the nearest specified char.

I thought about this question carefully and wrote it down. The idea is to scan the string, and every time I find a char, update the distance between the characters in front of it. So what should the distance of the previous characters be set to? Before the first char appears, it is infinity. For the rest, it is the distance to the previously recorded char. So the code is very simple to write. But when I first submitted it, I stepped on a trap. I used index == 0 to record the distance before the first char appeared. As a result, I didn't consider that the first character might be a char, so I made a mistake once and corrected it. That's over from now on.

My method looks like one pass at first glance, but every time a char is found, it will be traversed forward again, so it is equivalent to two passes.

class Solution {
    public int[] shortestToChar(String s, char c) {
        int[] array = new int[s.length()];
        int index = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == c) {
                for (int j = index; j <= i; j++) {
                    array[j] = Math.min(array[j], Math.abs(i - j));
                }
                index = i;
            } else {
                if (index == 0 && s.charAt(0) != c) {
                    array[i] = Integer.MAX_VALUE;
                } else {
                    array[i] = Math.abs(i - index);
                }
            }
        }
        return array;
    }
}

Then I looked at everyone’s solutions, and I thought, well, they’re pretty cool, but the ideas are pretty much the same, a bit like DP.

The main idea is to count once from the left, once from the right, and take the minimum of the two times. In fact, it is very similar to my own idea, but I feel that this way of thinking is more clever.

1. First initialize dist to the length of the array as the maximum possible distance (Integer.MAX_VALUE cannot be used here because the distance needs to be +1, and it will be gone if it overflows). Each time in the for loop, it is judged whether the current character is equal to char. If it is equal, then dist = 0. If it is not equal, then dist + 1, and then assign it to the array. It is equivalent to directly initializing all the characters on the right side of char to the left side. value. Then the dist is calculated in the same way in the second for. When it is finally put into the array, it is judged whether the current dist is smaller or the dist calculated from the left is smaller.

class Solution {
    public int[] shortestToChar(String s, char c) {
        int[] array = new int[s.length()];
        int dist = s.length();
        for (int i = 0; i < s.length(); i++) {
            dist = s.charAt(i) == c ? 0 : dist + 1;
            array[i] = dist;
        }
        dist = s.length();
        for (int i = s.length() - 1; i >= 0; i--) {
            dist = s.charAt(i) == c ? 0 : dist + 1;
            array[i] = Math.min(array[i], dist);
        }
        return array;
    }
}

This way of writing is not that intuitive and is a bit convoluted.

Another solution is more intuitive. For the first time, initialize everything first. If it is not char, remember max, if it is char, remember 0. Scan from left to right for the second time. If its value is not max, it means the right side can start recording, and then start updating the values ​​on the right. The update rule is dist[i + 1] = Math.min(dist[i + 1], dist[i] + 1). Swipe from right to left for the third time. Because the rightmost value must have a record instead of max, you can update it directly. The update rule is dist[i - 1] = Math.min(dist[i - 1], dist[ i] + 1). Hey, I still feel a bit convoluted after writing it... I still feel most comfortable with my own way of writing...

class Solution {
    public int[] shortestToChar(String s, char c) {
        int[] array = new int[s.length()];
        int dist = s.length();
        for (int i = 0; i < s.length(); i++) {
            array[i] = s.charAt(i) == c ? 0 : Integer.MAX_VALUE;
        }
        // update right part of the first occurance
        for (int i = 0; i < s.length() - 1; i++) {
            if (array[i] != Integer.MAX_VALUE) {
                array[i + 1] = Math.min(array[i + 1], array[i] + 1);
            }
        }
        for (int i = s.length() - 1; i > 0; i--) {
            array[i - 1] = Math.min(array[i - 1], array[i] + 1);
        }
        return array;
    }
}

Guess you like

Origin blog.csdn.net/qq_37333947/article/details/132473840