题目
给定一个字符串,找出不含有重复字符的最长子串的长度。
示例 1:
1 | 输入: "abcabcbb" |
示例 2:
1 | 输入: "bbbbb" |
示例 3:
1 | 输入: "pwwkew" |
分析
这道求让我们求最长的无重复字符的子串,注意这里是子串,不是子序列,所以必须是连续的。我们先不考虑代码怎么实现,如果给一个例子中的例子”abcabcbb”,让你手动找无重复字符的子串,该怎么找。
博主会一个字符一个字符的遍历,比如a,b,c,然后又出现了一个a,那么此时就应该去掉第一次出现的a,然后继续往后,又出现了一个b,则应该去掉一次出现的b,以此类推,最终发现最长的长度为3。所以说,我们需要记录之前出现过的字符,记录的方式有很多,最常见的是统计字符出现的个数,但是这道题字符出现的位置很重要,所以我们可以使用HashMap来建立字符和其出现位置之间的映射。进一步考虑,由于字符会重复出现,到底是保存所有出现的位置呢,还是只记录一个位置?我们之前手动推导的方法实际上是维护了一个滑动窗口,窗口内的都是没有重复的字符,我们需要尽可能的扩大窗口的大小。由于窗口在不停向右滑动,所以我们只关心每个字符最后出现的位置,并建立映射。窗口的右边界就是当前遍历到的字符的位置,为了求出窗口的大小,我们需要一个变量left来指向滑动窗口的左边界,这样,如果当前遍历到的字符从未出现过,那么直接扩大右边界,如果之前出现过,那么就分两种情况,在或不在滑动窗口内,如果不在滑动窗口内,那么就没事,当前字符可以加进来,如果在的话,就需要先在滑动窗口内去掉这个已经出现过的字符了,去掉的方法并不需要将左边界left一位一位向右遍历查找,由于我们的HashMap已经保存了该重复字符最后出现的位置,所以直接移动left指针就可以了。我们维护一个结果res,每次用出现过的窗口大小来更新结果res,就可以得到最终结果啦。
这里我们可以建立一个256位大小的整型数组来代替HashMap,这样做的原因是ASCII表共能表示256个字符,所以可以记录所有字符,然后我们需要定义两个变量res
和left
,其中res
用来记录最长无重复子串的长度,left
指向该无重复子串左边的起始位置,然后我们遍历整个字符串,对于每一个遍历到的字符,如果哈希表中该字符串对应的值为0,说明没有遇到过该字符,则此时计算最长无重复子串,i - left +1
,其中i
是最长无重复子串最右边的位置,left
是最左边的位置,还有一种情况也需要计算最长无重复子串,就是当哈希表中的值小于left
,这是由于此时出现过重复的字符,left
的位置更新了,如果又遇到了新的字符,就要重新计算最长无重复子串。最后每次都要在哈希表中将当前字符对应的值赋值为i+1
。
代码
1 | class Solution { |
代码运行过程
以”abcabcab”`为例:(这里以a来代替a的Ascll码值)
1 | 判断:m[a]==0(a没出现过) res=1 更新m{a}=1(a出现在第1个位置) |
个人总结
m[s[i]]
中的索引是以s[i]
的字符ASCII,其值为该字符在s中最后出现的位置
相当于HashMap中的key=字符,value=位置
1 | if (m[s[i]] == 0 || m[s[i]] < left) |
这个判断可以理解为如果 第i个字符从未出现过,或i在之前曾经出现过,
那么我们就将当前最长长度设置为从left-1
到i的长度i-left+1
重要的是我们要了解这个程序的逻辑结构———如何判别一个字符子串重复
其次,还要理解滑窗的概念,滑窗的确定需要两个值
- 起始位置
left
,即到目前为止,不重复的第一个字符 - 长度
res
,也是最后要返回的值