Java last==i后面的直觉?

Java last==i后面的直觉?,java,algorithm,Java,Algorithm,我正在解决一个问题: 给出了一个由小写字母组成的字符串S。我们希望将这个字符串分成尽可能多的部分,以便每个字母最多只出现在一个部分中,并返回一个表示这些部分大小的整数列表 示例: 输入:S=“ababcbacadefegdehijhklij” 输出:[9,7,8] 说明: 分区是“ababcbaca”、“defegde”、“hijhklij”。 这是一个分区,因此每个字母最多只显示一部分。 像“ababcbacadefegde”、“hijhklij”这样的分区是不正确的,因为它拆分为更少的部分

我正在解决一个问题:

给出了一个由小写字母组成的字符串S。我们希望将这个字符串分成尽可能多的部分,以便每个字母最多只出现在一个部分中,并返回一个表示这些部分大小的整数列表

示例:
输入:S=“ababcbacadefegdehijhklij”
输出:[9,7,8]
说明:
分区是“ababcbaca”、“defegde”、“hijhklij”。 这是一个分区,因此每个字母最多只显示一部分。 像“ababcbacadefegde”、“hijhklij”这样的分区是不正确的,因为它拆分为更少的部分

其中一项建议如下:

public List<Integer> partitionLabels(String S) {
        if(S == null || S.length() == 0){
            return null;
        }

        List<Integer> list = new ArrayList<>();
        int[] map = new int[26];  // record the last index of the each char

        for(int i = 0; i < S.length(); i++){
            map[S.charAt(i)-'a'] = i;
        }
        
        // record the end index of the current sub string
        int last = 0;
        int start = 0;
        for(int i = 0; i < S.length(); i++){
            last = Math.max(last, map[S.charAt(i)-'a']);
            if(last == i){
                list.add(last - start + 1);
                start = last + 1;
            }
        }
        return list;
    }
}
公共列表分区标签(字符串){
如果(S==null | | S.length()==0){
返回null;
}
列表=新的ArrayList();
int[]map=newint[26];//记录每个字符的最后一个索引
对于(int i=0;i

虽然我确实理解这个解决方案,但我对语句
last=Math.max(last,map[S.charAt(I)-'a')不是很满意
和子句
if(last==i)
。这里到底在做什么?

因此,要理解这个
for
循环到底在做什么,您必须了解
映射的设置方式。它使用此循环来填充它:

for(int i=0;i
现在,这也花了我一秒钟的时间,但请容忍我。它所做的是循环遍历
S
的每个字符。很简单。现在,它将获取索引,将
i
放入数组:
S.charAt(i)-'a'
。这是一个非常聪明的编程。它所做的是在当前位置获取角色。例如,如果我们在字符串中的索引
1
S.charAt(i)
将是
'b'
。然后我们从中减去
'a'
,这会将它们转换为UTF-16字符代码,并相互减去。这会将它们放置在数组中的位置
1
。然后将该索引设置为
i
。所以在字符串的索引1处,数组的元素1等于1。有点让人困惑,但让我们继续。如果我们在字符串的索引5处,则最后出现的是
'b'
。由于
'b'-'a'
仍然是1,它将覆盖索引1处的数组,但由于
i
已更改,因此那里的值也已更改。因为这是最后一个索引,所以我们可以知道数组中每个字符的最后一个索引

现在我们已经解决了数组填充的问题,让我们开始讨论您的实际问题。在下一个循环中,它将像第一次一样遍历数组,但这次它知道所有字符的最后一个索引。所以当我们运行语句时,
last=Math.max(last,map[S.charAt(i)-'a'),它所做的是使用前面描述的相同方法从数组中获取当前字符的最后一个索引。然后将其与当前
最后一个
值进行比较。这之所以特别是因为该值是持久的。因此,它得到了
'a'
的最后一个索引,它得到了
'b'
的最后一个索引,并得到了两者中较大的一个。这实际上是将它们放在它们的部分中的原因。因此,现在我们有了
last
作为当前部分的最后一个索引,我们可以将其与实际的当前索引进行比较。如果它们相等,我们就在本节末尾,可以将其添加到列表中

我希望这一切都能让你受益匪浅,不要犹豫,问任何问题

编辑: 让我们看一个例子。假设我们有字符串:
ababcbacadefegdehijhklij
。如果我们运行填充
map
数组的第一个循环,它将如下所示:

+----------------+---+---+---+----+----+----+----+----+----+----+----+----+
| Index          | 0 | 1 | 2 | 3  | 4  | 5  | 6  | 7  | 8  | 9  | 10 | 11 |
+----------------+---+---+---+----+----+----+----+----+----+----+----+----+
| Value          | 8 | 5 | 7 | 14 | 15 | 11 | 13 | 19 | 22 | 23 | 20 | 21 |
+----------------+---+---+---+----+----+----+----+----+----+----+----+----+
| Character      | a | b | c | d  | e  | f  | g  | h  | i  | j  | k  | l  |
| representation |   |   |   |    |    |    |    |    |    |    |    |    |
+----------------+---+---+---+----+----+----+----+----+----+----+----+----+
(注:字符表示仅用于参考索引)

当我们启动第二个for循环时,我们得到字符串中0处的字符,即
'a'
。然后,我们检查
映射
,查看它的最后一个索引在哪里,即8。因为当前索引是0而不是8,所以我们转到下一个索引。下一个字符是
'b'
,在
映射中的值为5。我们得到上一个值
last
(8)和5之间的最大值,以获得这两个字符组合的最后一个索引

让我们跳到第8位。这时我们已经看到了
'a'
'b'
,和
'c'
。他们最近的所有指数中最大的是
'a'
,为8。由于我们位于位置8,
last
的值为8,因此可以说
start
last
之间的字符是一个组,将其添加到列表中,并将
start
的值设置为
last+1
的索引。这是为了为下一组设置正确的开始位置

现在,如果我们移动到下一个索引,索引9,我们有一个我们以前从未见过的新角色。我们只是简单地重新开始这个过程,就好像我们处于位置1一样。索引9是
'd'
,最后一个索引是14。因为我们不在指数14,我们继续下一个。在索引10中,我们有
'e'
。这一个的最后一个索引是15,它比
'd'
的14大,所以我们取15,因为它更大。这基本上意味着如果
'd'
'e'
在一个组中,它至少必须一直到索引15,以便封装这两个组的所有字符。然后,它将通过re