使用双指针方法背后的直觉是:

huangapple go评论78阅读模式
英文:

Intuition behind using a two pointers approach

问题

我正在解答一个LeetCode.com上的问题:

> 给定一个由小写英文字母组成的字符串S。我们想将这个字符串尽可能多地分成若干部分,使得每个字母最多只出现在一部分中,并返回表示这些部分大小的整数列表。 <br>
对于输入: "ababcbacadefegdehijhklij",输出是: [9,7,8]

一个得到很多赞的解法如下:

public List&lt;Integer&gt; partitionLabels(String S) {
    if(S == null || S.length() == 0){
        return null;
    }
    List&lt;Integer&gt; list = new ArrayList&lt;&gt;();
    int[] map = new int[26];  // 记录每个字符的最后索引

    for(int i = 0; i &lt; S.length(); i++){
        map[S.charAt(i)-&#39;a&#39;] = i;
    }
    // 记录当前子字符串的结束索引
    int last = 0;
    int start = 0;
    for(int i = 0; i &lt; S.length(); i++){
        last = Math.max(last, map[S.charAt(i)-&#39;a&#39;]);
        if(last == i){
            list.add(last - start + 1);
            start = last + 1;
        }
    }
    return list;
}

我理解我们在第一个for循环中的操作(我们只是存储了每个字符最后出现的索引),但对于第二个循环我不太确定:

a. 为什么我们要计算max()并比较last==i
b. 这如何帮助我们实现我们的目标 - 在上面的示例中,当我们在位置8(从0开始索引)遇到a时,什么保证我们不会在大于8的位置遇到b呢? 因为如果确实如此,那么将8视为子字符串的结束位置就是错误的。

谢谢!

英文:

I am solving a question on LeetCode.com:

>A string S of lowercase English letters is given. We want to partition this string into as many parts as possible so that each letter appears in at most one part, and return a list of integers representing the size of these parts. <br>
For the input: "ababcbacadefegdehijhklij" the output is: [9,7,8]

A highly upvoted solution is as below:

public List&lt;Integer&gt; partitionLabels(String S) {
    if(S == null || S.length() == 0){
        return null;
    }
    List&lt;Integer&gt; list = new ArrayList&lt;&gt;();
    int[] map = new int[26];  // record the last index of the each char

    for(int i = 0; i &lt; S.length(); i++){
        map[S.charAt(i)-&#39;a&#39;] = i;
    }
    // record the end index of the current sub string
    int last = 0;
    int start = 0;
    for(int i = 0; i &lt; S.length(); i++){
        last = Math.max(last, map[S.charAt(i)-&#39;a&#39;]);
        if(last == i){
            list.add(last - start + 1);
            start = last + 1;
        }
    }
    return list;
}

I understand what we are doing in the first for loop (we just store the index of the last occurrence of a character), but I am not too sure about the second one:

a. Why do we calculate the max() and compare last==i?
b. How does it help us achieve what we seek - in the above example, when we encounter a at position 8 (0-indexed), what guarantees that we won't encounter, say b, at a position greater than 8? Because, if we do, then considering 8 as the end position of our substring is incorrect.

Thanks!

答案1

得分: 0

如果我们遇到字符S[i],我们只能在其最后出现之后对字符串进行切割,因此是map[S.charAt(i)-'a']。我们在last中取最大值,因为我们需要确保所有处理过的字符在前缀中具有它们的最后出现位置,所以我们要找到这些索引中最靠右的位置,因此使用了max。如果我们遇到这样的字符S[i],使得i是它的最后出现位置,并且在i之前的所有字符都在i之前已经出现过,我们可以将子字符串start..i添加到结果中,并且将start = i + 1 用于下一个子字符串。

英文:

If we encounter a character S[i] we may cut the string only after it's last occurence, hence map[S.charAt(i)-&#39;a&#39;]. We maximize the value in last because we need to ensure that all the processed characters will have their last occurence in the prefix, so we look at the rightmost of such indices, hence the max. If we encounter a character S[i] such that i is it's last occurrence and all characters before have their last occurrences before i, we may add the substring start..i to the result, and set start = i + 1 for the next substring.

答案2

得分: 0

这个想法是这样的。每当特定字符的最后一次出现与当前索引相匹配时,意味着该特定字符只出现在这一部分中。

为了更好地理解这一点,只需执行以下操作。

int last = 0;
int start = 0;
for(int i = 0; i < S.length(); i++){
   last = Math.max(last, map[S.charAt(i)-'a']);
   System.out.println(last+" "+i);
   if(last == i){
      list.add(last - start + 1);
	  start = last + 1;
   }
}

以你的示例字符串"ababcbacadefegdehijhklij"为例。

现在,输出将会是

8 0
8 1
8 2
8 3
8 4
8 5
8 6
8 7
8 8
14 9
15 10
15 11
15 12
15 13
15 14
15 15
19 16
22 17
23 18
23 19
23 20
23 21
23 22
23 23

字符a的最后一次出现在第8个位置。现在我们在第0个位置。增加i的值。所以,在达到第8个位置之前,我们无法确定每个部分是否最多包含1个字符。假设下一个字符是b,并且它最终出现在第10个位置,那么我们需要确认直到第10个位置。

if(last == i){

}

上面的if条件只是确认当前部分已结束,我们可以从下一个索引开始一个新的部分。在执行此操作之前,我们将当前部分的长度添加到输出中。

英文:

The idea is this. Whenever the last occurrence of a particular character matches the current index, it means that this particular character appears only in this part.

To understand this better, just do this.

int last = 0;
int start = 0;
for(int i = 0; i &lt; S.length(); i++){
   last = Math.max(last, map[S.charAt(i)-&#39;a&#39;]);
   System.out.println(last+&quot; &quot;+i);
   if(last == i){
      list.add(last - start + 1);
	  start = last + 1;
   }
}

Take your example string &quot;ababcbacadefegdehijhklij&quot;.

Now, the output would be

8 0
8 1
8 2
8 3
8 4
8 5
8 6
8 7
8 8
14 9
15 10
15 11
15 12
15 13
15 14
15 15
19 16
22 17
23 18
23 19
23 20
23 21
23 22
23 23

The last occurrence of a is at the 8th position. Now we are at 0th position. Increment i. So, the till we get to 8th position, we cant be sure that each part contains at most 1 character. Suppose the next character is b and it occurs finally in 10th position, then we need to confirm till 10th position.

if(last == i){

}

The above if just confirms that the part is over and we can start a new part from the next index. Before doing this, we are adding the length of the current part to the output.

huangapple
  • 本文由 发表于 2020年7月23日 13:03:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/63047202.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定