Given a string, find the length of the longest substring without repeating characters.

Example 1:

Input: "abcabcbb"
Output: 3
Explanation: The answer is "abc", with the length of 3.

Example 2:

Input: "bbbbb"
Output: 1
Explanation: The answer is "b", with the length of 1.

Example 3:

Input: "pwwkew"
Output: 3
Explanation: The answer is "wke", with the length of 3.
Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

这道求最长无重复子串的题和之前那道 Isomorphic Strings 很类似,属于 LeetCode 早期经典题目,博主认为是可以跟 Two Sum 媲美的一道题。给了我们一个字符串,让求最长的无重复字符的子串,注意这里是子串,不是子序列,所以必须是连续的。先不考虑代码怎么实现,如果给一个例子中的例子 "abcabcbb",让你手动找无重复字符的子串,该怎么找。博主会一个字符一个字符的遍历,比如 a,b,c,然后又出现了一个a,那么此时就应该去掉第一次出现的a,然后继续往后,又出现了一个b,则应该去掉一次出现的b,以此类推,最终发现最长的长度为3。所以说,需要记录之前出现过的字符,记录的方式有很多,最常见的是统计字符出现的个数,但是这道题字符出现的位置很重要,所以可以使用 HashMap 来建立字符和其出现位置之间的映射。进一步考虑,由于字符会重复出现,到底是保存所有出现的位置呢,还是只记录一个位置?我们之前手动推导的方法实际上是维护了一个滑动窗口,窗口内的都是没有重复的字符,需要尽可能的扩大窗口的大小。由于窗口在不停向右滑动,所以只关心每个字符最后出现的位置,并建立映射。窗口的右边界就是当前遍历到的字符的位置,为了求出窗口的大小,需要一个变量 left 来指向滑动窗口的左边界,这样,如果当前遍历到的字符从未出现过,那么直接扩大右边界,如果之前出现过,那么就分两种情况,在或不在滑动窗口内,如果不在滑动窗口内,那么就没事,当前字符可以加进来,如果在的话,就需要先在滑动窗口内去掉这个已经出现过的字符了,去掉的方法并不需要将左边界 left 一位一位向右遍历查找,由于 HashMap 已经保存了该重复字符最后出现的位置,所以直接移动 left 指针就可以了。维护一个结果 res,每次用出现过的窗口大小来更新结果 res,就可以得到最终结果啦。

这里可以建立一个 HashMap,建立每个字符和其最后出现位置之间的映射,然后需要定义两个变量 res 和 left,其中 res 用来记录最长无重复子串的长度,left 指向该无重复子串左边的起始位置的前一个,由于是前一个,所以初始化就是 -1,然后遍历整个字符串,对于每一个遍历到的字符,如果该字符已经在 HashMap 中存在了,并且如果其映射值大于 left 的话,那么更新 left 为当前映射值。然后映射值更新为当前坐标i,这样保证了 left 始终为当前边界的前一个位置,然后计算窗口长度的时候,直接用 i-left 即可,用来更新结果 res。

这里解释下程序中那个 if 条件语句中的两个条件 m.count(s[i]) && m[s[i]] > left,因为一旦当前字符 s[i] 在 HashMap 已经存在映射,说明当前的字符已经出现过了,而若 m[s[i]] > left 成立,说明之前出现过的字符在窗口内,那么如果要加上当前这个重复的字符,就要移除之前的那个,所以让 left 赋值为 m[s[i]],由于 left 是窗口左边界的前一个位置(这也是 left 初始化为 -1 的原因,因为窗口左边界是从0开始遍历的),所以相当于已经移除出滑动窗口了。举一个最简单的例子 "aa",当 i=0 时,建立了 a->0 的映射,并且此时结果 res 更新为1,那么当 i=1 的时候,发现a在 HashMap 中,并且映射值0大于 left 的 -1,所以此时 left 更新为0,映射对更新为 a->1,那么此时 i-left 还为1,不用更新结果 res,那么最终结果 res 还为1,正确,代码如下:

C++ 解法一:

class Solution {
public:
int lengthOfLongestSubstring(string s) {
int res = , left = -, n = s.size();
unordered_map<int, int> m;
for (int i = ; i < n; ++i) {
if (m.count(s[i]) && m[s[i]] > left) {
left = m[s[i]];
}
m[s[i]] = i;
res = max(res, i - left);
}
return res;
}
};

下面这种写法是上面解法的精简模式,这里我们可以建立一个 256 位大小的整型数组来代替 HashMap,这样做的原因是 ASCII 表共能表示 256 个字符,但是由于键盘只能表示 128 个字符,所以用 128 也行,然后全部初始化为 -1,这样的好处是不用像之前的 HashMap 一样要查找当前字符是否存在映射对了,对于每一个遍历到的字符,直接用其在数组中的值来更新 left,因为默认是 -1,而 left 初始化也是 -1,所以并不会产生错误,这样就省了 if 判断的步骤,其余思路都一样:

C++ 解法二:

class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector<int> m(, -);
int res = , left = -;
for (int i = ; i < s.size(); ++i) {
left = max(left, m[s[i]]);
m[s[i]] = i;
res = max(res, i - left);
}
return res;
}
};

Java 解法二:

public class Solution {
public int lengthOfLongestSubstring(String s) {
int[] m = new int[256];
Arrays.fill(m, -1);
int res = 0, left = -1;
for (int i = 0; i < s.length(); ++i) {
left = Math.max(left, m[s.charAt(i)]);
m[s.charAt(i)] = i;
res = Math.max(res, i - left);
}
return res;
}
}

下面这种解法使用了 HashSet,核心算法和上面的很类似,把出现过的字符都放入 HashSet 中,遇到 HashSet 中没有的字符就加入 HashSet 中并更新结果 res,如果遇到重复的,则从左边开始删字符,直到删到重复的字符停止:

C++ 解法三:

class Solution {
public:
int lengthOfLongestSubstring(string s) {
int res = , left = , i = , n = s.size();
unordered_set<char> t;
while (i < n) {
if (!t.count(s[i])) {
t.insert(s[i++]);
res = max(res, (int)t.size());
} else {
t.erase(s[left++]);
}
}
return res;
}
};

Java 解法三:

public class Solution {
public int lengthOfLongestSubstring(String s) {
int res = 0, left = 0, right = 0;
HashSet<Character> t = new HashSet<Character>();
while (right < s.length()) {
if (!t.contains(s.charAt(right))) {
t.add(s.charAt(right++));
res = Math.max(res, t.size());
} else {
t.remove(s.charAt(left++));
}
}
return res;
}
}

Github 同步地址:

https://github.com/grandyang/leetcode/issues/3

类似题目:

Longest Substring with At Most Two Distinct Characters

Longest Substring with At Most K Distinct Characters

Subarrays with K Different Integers

参考资料:

https://leetcode.com/problems/longest-substring-without-repeating-characters/

https://leetcode.com/problems/longest-substring-without-repeating-characters/discuss/1737/C++-code-in-9-lines.

https://leetcode.com/problems/longest-substring-without-repeating-characters/discuss/1812/Share-my-Java-solution-using-HashSet

https://leetcode.com/problems/longest-substring-without-repeating-characters/discuss/1729/11-line-simple-Java-solution-O(n)-with-explanation

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Longest Substring Without Repeating Characters 最长无重复字符的子串的更多相关文章

  1. [LeetCode] Longest Substring Without Repeating Characters 最长无重复字符的子串 C++实现java实现

    最长无重复字符的子串 Given a string, find the length of the longest substring without repeating characters. Ex ...

  2. [LeetCode] Longest Substring Without Repeating Characters 最长无重复子串

    Given a string, find the length of the longest substring without repeating characters. For example, ...

  3. [LeetCode] Longest Substring Without Repeating Characters最长无重复子串

    Given a string, find the length of the longest substring without repeating characters. For example, ...

  4. 3. Longest Substring Without Repeating Characters - 最长无重复字符子串-Medium

    Examples: Description: Given a string, find the length of the longest substring without repeating ch ...

  5. [LeetCode] 3.Longest Substring Without Repeating Characters 最长无重复子串

    Given a string, find the length of the longest substring without repeating characters. Example 1: In ...

  6. 【LeetCode每天一题】Longest Substring Without Repeating Characters(最长无重复的字串)

    Given a string, find the length of the longest substring without repeating characters. Example 1:    ...

  7. LeetCode Longest Substring Without Repeating Characters 最长不重复子串

    题意:给一字符串,求一个子串的长度,该子串满足所有字符都不重复.字符可能包含标点之类的,不仅仅是字母.按ASCII码算,就有2^8=128个. 思路:从左到右扫每个字符,判断该字符距离上一次出现的距离 ...

  8. 【LeetCode】3.Longest Substring Without Repeating Characters 最长无重复子串

    题目: Given a string, find the length of the longest substring without repeating characters. Examples: ...

  9. leetcode 3 Longest Substring Without Repeating Characters最长无重复子串

    Given a string, find the length of the longest substring without repeating characters. For example, ...

随机推荐

  1. tfs2015 生成与发布 配置

    先来看一张微软官方的自动生成与发布架构图,以便了解很多概念间的关系 1.安装好TFS2015(可以参考TFS2010的安装过程,尤其是账号权限相关),我自己是从TFS2010一路升级上来的(TFS20 ...

  2. 基于物理的渲染—HDR Tone Mapping

    在游戏引擎渲染管线中,我们对于R.G.B通道颜色信息的数值范围通常设置在[0,1]之间(或者是[0,255]).其中,0代表没有光亮度,1代表显示器能够显示的最大光亮度.这个表示方式虽然直接易懂,但它 ...

  3. python-写入excel(xlswriter)

     一.安装xlrd模块: 1.mac下打开终端输入命令: pip install XlsxWriter 2.验证安装是否成功: 在mac终端输入 python  进入python环境 然后输入 imp ...

  4. JAVA中使用Log4j2日志和Lombok引入日志的方法

    一.简述 我们项目中既要使用lombok,又要使用log4j2时,使用日志将会更简单. 二.解决 1.引入依赖 <dependency> <groupId>org.apache ...

  5. php读取ini配置文件属性

    ini的内容格式如下,请根据自己的INI,格式修改下段程序. autostart = false font_size = font_color = red =================== fu ...

  6. FileClassify文件日期分类工具

    FileClassify是一款免费的文件按日期分类工具,能够根据文件修改日期,将文件移动或复制到对应的目录中 如果对您有较大的帮助,欢迎捐赠我们,我们对您表示衷心的感谢! 1.输入文件夹和输出文件可以 ...

  7. 再看C# ThreadPool与Task的认识总结

    工作线程与I/O线程     在ThreadPool中有这样一个方法: public static bool SetMaxThreads(int workerThreads, int completi ...

  8. 阿里云 nginx配置ssl证书实现https访问

    一,环境说明 服务器系统:ubuntu16.04LTS 服务器IP地址:47.89.12.99 域名:bjubi.com 二,域名解析到服务器 在阿里云控制台-产品与服务-云解析DNS-找到需要解析的 ...

  9. R语言︱处理缺失数据&&异常值检验、离群点分析、异常值处理

    在数据挖掘的过程中,数据预处理占到了整个过程的60% 脏数据:指一般不符合要求,以及不能直接进行相应分析的数据 脏数据包括:缺失值.异常值.不一致的值.重复数据及含有特殊符号(如#.¥.*)的数据 数 ...

  10. 【C#】解析C#中管道流的使用

    目录结构: contents structure [+] 匿名管道(anonymous pipe) 命名管道(named pipe) 管道为进程间通信提供了一种可能.管道分为两种,一种是匿名管道,另一 ...