题目等级:Medium

题目描述:

  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.

  题意:给定一个字符串,找到其中不含重复字符的最长子串的长度,要注意子串和子序列的区别。


解题思路:

  一般对于这种稍微复杂一些的题目,首先可以想到的是最基础的暴力解法,然后我们可以在暴力解法的基础上思考相应的改进和提升效率的算法,针对本题我们给出以下三种解法。

  解法一:Brute Force暴力解法

  暴力解法自然是最直观的,既然是求不含重复字符的最长子串,我们可以通过穷举所有的子串,判断其中是否含有重复字符,并记录最大长度。假设我们现在有一个函数能判断一个字符串中是否包含重复字符,然后只需要一个两层循环枚举所有的子串,即可得到结果

  那么这个判断一个字符串是否包含重复字符的函数该怎么实现呢,首先要判断一个字符串是否包含重复字符,总得每一个字符都比较过才能知道重复与否,因此大致这个算法应该是O(n)复杂度。再进一步思考,要判断是否重复,集合不正好是不包含重复元素的数据结构吗,因此,只需用一个set保存之前的元素,我们就可以知道是否重复了。

  对于这个暴力解法,首先要穷举所有的子串需要两层循环,每个子串判断重复与否还需要一层循环,因此,整体时间复杂度是O(n^3),由于使用了一个Set,空间复杂度为O(n)。

  然而,我们把以下的代码实现在LeetCode上提交,会发现提示Time Limit Exceeded,因为超时无法通过。

class Solution {
public int lengthOfLongestSubstring(String s) {
if(s==null || s.length()==0)
return 0;
int len=s.length();
int res=1;
for(int i=0;i<len;i++){
for(int j=i+1;j<len;j++){
if(allUnique(s,i,j))
res=Math.max(res,j-i+1);
}
}
return res;
} public boolean allUnique(String s,int i,int j){
Set<Character> set=new HashSet<>();
for(;i<=j;i++){
char c=s.charAt(i);
if(set.contains(c))
return false;
set.add(c);
}
return true;
}
}

  解法二:滑动窗口解法

  暴力法由于超时无法通过,因此我们必须改进算法,提升时间效率。回过头来,我们再看暴力解法,可以发现实际上这个解法做了很多重复的和不必要的工作,主要体现在以下两个方面:

  (1)分别判断每一个子串是否重复,这里面是有很多重复的工作的,实际上子串和子串之间不是孤立的,是有联系的。比如,如果我们已经判断了字符串str中从i到j(i和j是下标)是不包含重复字符的,那么在判断从i到j+1时,完全没必要从头在来判断这个子串,因为我们只需要知道第j+1个字符是否在从i到j中出现过就可以了

  (2)如果我们已经判断了字符串str中从i到j(i和j是下标)是包含重复字符的,那么以第i个字符开始,以第j个字符之后的所有字符结尾的字符串都没必要再判断了,因为它一定也是包含重复字符的。此时直接增大i的值就可以了。

  综合以上两点,实际上我们不难得出,这实际上就是一个滑动窗口从前向后移动的过程,如下图所示:

  由此,我们可以得到如下所示的代码实现,即滑动窗口解法。其中只有一层循环,并且最坏的情况下循环次数为2n次(即j先加到最大,然后i由逐步加大,各需要n次),所以,时间复杂度为O(2n),仍然用了一个set保存字符,所以空间复杂度为O(n)。

class Solution {
public int lengthOfLongestSubstring(String s) {
if(s==null || s.length()==0)
return 0;
int len=s.length();
int i=0,j=0;
Set<Character> set=new HashSet<>();
int res=0;
while(i<len&&j<len){
char c=s.charAt(j);
if(set.contains(c)){ //注意这里,并不只是i+1,对应的字符也要从集合中删掉
set.remove(s.charAt(i));
i++;
}else{
set.add(c);
res=Math.max(res,j-i+1);
j++;
}
}
return res;
}
}

  解法三:改进的滑动窗口

  解法二实际上已经将复杂度降到了O(n)级别,正如我们前面所说,要判断每个字符是否重复,最起码每个字符都要比较到,否则不可能知道重复与否,因此直观上我们应该有一个大概的认识:此题的算法很有可能应该是不会比O(n)更优了,当然这只是一个粗略的想法。

  解法三实际上也就是针对解法二的一个地方进行了优化。考虑如下情况:

  此时,j指向c,根据滑动窗口解法,前面的窗口中存在c,因此重复,此时应该i增大1,指向b,但是很明显,从b到j中还是包含重复的c,因此一定是仍然重复的,所以当遇到重复时,i没必要只加一,可以一步到位,直接定位到重复字符的下一个去,比如这里,可以直接指向d,因为前面无论哪个都包含c,都会重复。

  因此,我们就可以得到对应的解法三,这里由于需要保存字符,还需要保存位置,因此必须将set改为Map<字符,下标>。这样,我们看到,实际上i是跳跃向前的,因此最坏情况下也只需要n次循环,时间复杂度由O(2n)降到了O(n),空间复杂度只是由set换为map,仍为O(n)。

  代码如下:

class Solution {
public int lengthOfLongestSubstring(String s) {
if(s==null||s.length()==0)
return 0;
int len=s.length();
int i=0,j=0,res=0;
Map<Character,Integer> map=new HashMap<>();
while(j<len){
char c=s.charAt(j);
if(map.containsKey(c)){
i=Math.max(i,map.get(c)+1);
}
res=Math.max(res,j-i+1);
map.put(c,j);
j++;
}
return res;
}
}

  除此之外,正如很多题中用到的,如果针对于一些特殊的字符集,比如ASCLL码,就可以用字符数组代替哈希表,字符的ASCII码作为下标,数组值为字符的位置。

总结

  对于本题,这三种解法实际上正是LeetCode的题解中给出的,从暴力到比较巧妙的滑动窗口,再到对其进行改进,跳跃向前,时间复杂度逐步降低,这里,我认为重点不是如何解决这一道题,而是如何分析,从哪里能给改进,一步一步寻找最佳解法。

【LeetCode】3 、Longest Substring Without Repeating Characters的更多相关文章

  1. LeetCode 第 3 题(Longest Substring Without Repeating Characters)

    LeetCode 第 3 题(Longest Substring Without Repeating Characters) Given a string, find the length of th ...

  2. 【LeetCode从零单排】No 3 Longest Substring Without Repeating Characters

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

  3. Leetcode经典试题:Longest Substring Without Repeating Characters解析

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

  4. LeetCode(3)Longest Substring Without Repeating Characters

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

  5. leetcode第三题Longest Substring Without Repeating Characters java

    Longest Substring Without Repeating Characters Given a string, find the length of the longest substr ...

  6. leetcode第三题--Longest Substring Without Repeating Characters

    Problem:Given a string, find the length of the longest substring without repeating characters. For e ...

  7. LeetCode解题笔记 - 3. Longest Substring Without Repeating Characters

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

  8. LeetCode第三题—— Longest Substring Without Repeating Characters(最长无重复子字符串)

    题目描述 Given a string, find the length of the longest substring without repeating characters. Example ...

  9. leetcode - 3、Longest Substring Without Repeating Characters

    题目链接:https://leetcode.com/problems/longest-substring-without-repeating-characters/description/ 题目要求: ...

随机推荐

  1. CentOS 7下安装Hadoop2.2

    这里就介绍CentOS的安装了,直接进入Hadoop2.2伪分布模式安装. 1.安装包下载 1.1.下载JDK1.7 眼下JDK的版本号是jdk1.8.0_25.这里下载的是jdk1.7.0_67. ...

  2. ansible安装测试

    安装: # yum install ansible # yum install sshpass 配置: # vi /etc/ansible/hosts  [mysqldb] 172.16.100.23 ...

  3. uboot流程分析--修改android启动模式按键【转】

    本文转载自:http://blog.csdn.net/dkleikesa/article/details/9792747 本人用的android平台用的bootloader用的是uboot,貌似大多数 ...

  4. Jsp内置对象和EL隐藏(内置)对象

      JSP中的内置对象一共有九个, 由于有的不太常用, 所以总是记不住, 从Sun公司的网站上找到的PDF文档, 把这一部分放在这里, 以备随时查用:     JSP九个内置对象: Implicit ...

  5. web跨域问题回顾

    晚上看spring web源码时看到了cors包,查了一下原来是在4.2之后新加的用来更方便让web应用服务支持cors协议的.于是有了下面几个问题. web跨域问题的起源是因为浏览器为了安全而遵循的 ...

  6. CSS盒子居中的常用的几种方法

    第一种: 用css的position属性 <style type="text/css"> .div1 { width: 100px; height: 100px; bo ...

  7. P3379最近公共祖先(LCA)

    题目描述 如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 输入输出格式 输入格式: 第一行包含三个正整数N.M.S,分别表示树的结点个数.询问的个数和树根结点的序号. 接下来N-1行每 ...

  8. 【BZOJ2944】[Poi2000]代码(卡特兰数)

    这题在网上找不到题解,硬写一下午终于写出来了-- 题目: BZOJ2944 分析: 首先明确: 比较两棵节点数相同的二叉树时,根节点是第一关键字,左子树是第二关键字,右子树是第三关键字: 然后我们分析 ...

  9. ACM_百度的面试(单调栈)

    百度的面试 Time Limit: 2000/1000ms (Java/Others) Problem Description: 在一个二维平面,从左到右竖立n根高度分别为:a[1],a[2],... ...

  10. 题解报告:hdu 1028 Ignatius and the Princess III(母函数or计数DP)

    Problem Description "Well, it seems the first problem is too easy. I will let you know how fool ...