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

我的解法:

 class Solution {
HashSet<Character> set = new HashSet<Character>();
public int lengthOfLongestSubstring(String s) {
int len = s.length();
char[] string = new char[len];
s.getChars(0,len,string,0);
int ans = 0;
//左右边界,左闭右开
int left = 0,right = 0;
for(int i=0;i<len;++i){
if(set.add(string[i])){
//添加成功,此字符未出现过,右边界扩张1
++right;
ans = ans>(right-left)?ans:(right-left);
}else{
//添加进集合失败,即出现了重复字符
//调整左边界:从右边界现在的位置向后调整
for(int j=right-1;j>=left;--j){
if(string[j]==string[i]){
left = j+1;
break;
}
}
++right;
ans = ans>(right-left)?ans:(right-left);
}
}
return ans;
}
}

维护左右边界两个数,然后每次添加进去一个新字符就更新边界,计算长度并与ans比较更新 ans

时间复杂度O(n2),因为最坏情况下每次重复的字符都是在子串的第一个的位置,如"abcdefgabcdefg",那第二个循环必须向前找很远.

空间复杂度O(n),因为用了HashSet可能比手动维护的数组的形式占得空间大一点吧

运行错误一次: 一开始省事儿从第二个字符开始检查,结果忘记了考虑空串,傻了傻了

答案错误一次: 低级错误,不说了太丢脸了(左闭右开算长度减完了我咋还+1了,爆哭)

结果:

官方题解:

https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-by-leetcod/

方法1:

暴力法,代码略,考察每个子串是否满足无重复字符,会超时的.

方法2:

滑动窗口法.和我这个解法的思路是一样的,仔细想了下为什么我复杂度高然后发现改成这样就会变成O(n)了

我的方法 - 受方法2启发改:

 class Solution {
HashSet<Character> set = new HashSet<Character>();
public int lengthOfLongestSubstring(String s) {
int len = s.length();
char[] string = new char[len];
s.getChars(0,len,string,0);
int ans = 0;
//左右边界,左闭右开
int left = 0,right = 0;
for(int i=0;i<len;++i){
if(set.add(string[i])){
//添加成功,此字符未出现过,右边界扩张1
++right;
ans = ans>(right-left)?ans:(right-left);
}else{
//添加进集合失败,即出现了重复字符
//调整左边界:从左边界现在的位置向前调整
for(int j=left;j<right;++j){
if(string[j]==string[i]){
left = j+1;
break;
}
}
++right;
ans = ans>(right-left)?ans:(right-left);
}
}
return ans;
}
}

这样这个方法时间复杂度就是O(2n) = O(n)的了.空间复杂度O(n)

原因在于这个虽然是两重循环,但是第二重循环每次必定是从新的地方开始,不走回头路.

如: "abcdefgabcdefg",对我的原始方法来说,当检查到第二个a的时候左边界必须向左后退到第一个a,然而下一个检查b,左边界又要向左后退到第一个b,其中[第一个b,第二个a)这些后退都是上一步检查第二个a的时候做过的了,这里在走回头路做无用功,所以浪费了时间.

对改进方法来说,检查第二个a只需要看第一个a,检查第二个b的时候只需要看第一个b,不必走回头路.

即使是对改进方法而言最坏的情况如"abcdefghijklmnnop"来说,即使检查第二个n需要从第一个a一直前进左边界直到第一个n,但是只后再也不会检查这些元素是否在左边界以左了.

体会到了"滑动窗口"这个命名的妙处.比单独的两个边界的名字有整体性.

结果:

方法3:

优化的滑动窗口,利用map而不是set,从根本上省去了第二个循环

受教,学到了的方法:

 class Solution {
HashMap<Character,Integer> map = new HashMap<Character,Integer>();
public int lengthOfLongestSubstring(String s) {
int len = s.length();
char[] string = new char[len];
s.getChars(0,len,string,0);
int ans = 0;
//左右边界,左闭右开
int left = 0,right = 0;
for(int i=0;i<len;++i){
if(map.containsKey(string[i])){
//找到第一次出现时的位置
//左边界不能变小,不能走回头路
//在这个前提下把左边界变为上次出现此字符的位置再加一
left = map.get(string[i]) + 1>left?map.get(string[i]) + 1:left;
}
//如果原本就有这个字符,那么这个put是更新此字符的位置,否则是加入一个新的字符并记录它的位置
map.put(string[i],i);
right = i+1;
ans = ans>(right - left)?ans:(right - left);
}
return ans;
}
}

left的更新中,left不可以变小很重要.

时间复杂度O(n),空间复杂度O(n)

答案错误一次: 忘记考虑left不可以变小了...

结果:

(诡异的是速度慢于上一个.可能containsKey的开销在数据量比较小的时候真的不能忽略吧)

方法4:

优化的滑动窗口法,不使用HashMap,转而使用一个数组,思路跟上面其实一样,更原始一点所以效率更优一点.

代码略

时间复杂度O(n),空间复杂度O(n)

2019年7月20日 - LeetCode0003的更多相关文章

  1. 【2019年05月20日】A股滚动市盈率PE历史新低排名

    2010年01月01日 到 2019年05月20日 之间,滚动市盈率历史新低排名. 上市三年以上的公司, 2019年05月20日市盈率在300以下的公司. 1 - 阳光照明(SH600261) - 历 ...

  2. tmux使用——2019年11月20日16:40:15

    1.tmux 命令行的典型使用方式是,打开一个终端窗口(terminal window,以下简称"窗口"),在里面输入命令.用户与计算机的这种临时的交互,称为一次"会话& ...

  3. 2019年7月20日针对iPhone7/7P有锁机的爆破限制

    背景 2019年7月20号苹果更改了激活策略,致使卡贴机一夜回到解放前,目前只能使用tmsi或者tmsi+iccid模式激活手机,但是缺点是移动联通信号真的不稳定,漏接电话,无法开启热点等等毛病.尤其 ...

  4. 【搬家啦】2019年06月20日csdn难民来报道啦~

    原博地址:https://blog.csdn.net/the_fool_

  5. 2019年7月20日 - LeetCode0002

    https://leetcode-cn.com/problems/add-two-numbers/submissions/ 我的方法: /** * Definition for singly-link ...

  6. 易初大数据 2019年10月20日 linux死亡导图 王庆超

  7. 易初大数据 2019年10月20日 spss习题 王庆超

    一.选择题 1.有关spss数据字典的说法,正确的是:D A.SPSS数据集的数据字典可以复制到其他数据集中 B.SPSS数据集的数据字典是不能复制的 C.SPSS的数据字典可以通过“复制”和“黏贴” ...

  8. 西安活动 | 4月20日「拥抱开源,又见.NET :云时代 • 新契机」

    云计算日渐兴起,成为提升企业效率和生产力的最终解决方案,而云时代也为软件开发模式带来了翻天覆地的变化.可以说 .NET Core就是这个时代催生的产物.自2016年 .NET Core 1.0 发布以 ...

  9. 【08月20日】A股滚动市净率PB历史新低排名

    2010年01月01日 到 2019年08月20日 之间,滚动市净率历史新低排名. 上市三年以上的公司,2019年08月20日市净率在30以下的公司. 来源:A股滚动市净率(PB)历史新低排名. 1 ...

随机推荐

  1. SimpleMembership,成员资格提供程序、 通用的提供者和新的 ASP.NET 4.5 Web 窗体和 ASP.NET MVC 4 模板

    ASP.NET MVC 4 互联网模板中添加一些新的. 非常有用的功能,构建 SimpleMembership.这些更改将添加一些很有特色,像很多更简单. 可扩展会员 API 和 OAuth 的支持. ...

  2. Uncaught (in promise)

    Uncaught (in promise) 使用es6的promise时候,有时候会出现如下错误: 这是因为,使用定义promise方法的时候,reject了,但是,在使用的地方没有用catch进行接 ...

  3. Delphi Thread.Queue与Synchronize的区别(差别: Synchronize是阻塞,Queue是非阻塞)

    前话:  其实大家要学会看源码, 我接下来要说的这些东东,与其等别人讲,还不如自己搞几个代码试一下,印象还深刻点 TThread.Queue和TThread.Synchronize的区别, 效果上:二 ...

  4. PHP开发框架 Laravel

    Laravel 是一套简洁.优雅的PHP Web开发框架(PHP Web Framework).它可以让你从面条一样杂乱的代码中解脱出来:它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁.富 ...

  5. Bitmap的读写和几个小儿科的滤镜效果~

    闲来玩玩图像处理,拿破仑说过:“不想自己实现滤镜的美工不是好程序员~~#@!*^...#&!@......”  因为在学校做过很多美工的工作,而且从小就喜欢画画所以对图像相关的东西都还比较感兴 ...

  6. spring cloud之eureka简介

    最近线上的接口出了一些问题,有一些可能不是代码的问题,但是由于是测试和其他方面的同事爆出来的,所以感觉对接口的监控应该提上日程. 经过搜索发现,spring cloud的eureka就是专门做这方面工 ...

  7. 03 Javascript的数据类型

    数据类型包括:基本数据类型和引用数据类型 基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构成的对象. 当我们把变量赋值给一个变量时,解析器首先要确认的就是这个值是基本类型值还是引用类型值 ...

  8. Fire! UVA - 11624 (两步bfs)

    题目链接 题意 人要从迷宫走出去,火会向四个方向同时扩散 分析 两步bfs,先出火到达各地时的时间(设初始时间为0,人每走一步为1s,在着一步内火可以向四周可触及的方向同时扩散),然后在bfs人,人能 ...

  9. C++学习笔记 之 运算符

    用来执行特定的数字或逻辑操作,C++主要提供的运算符如下: 算数运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 杂项运算符 算数运算符: 主要运算符:+(加).-(减) .*(乘) ./(除) ...

  10. Codeforces Gym100623J:Just Too Lucky(数位DP)

    http://codeforces.com/gym/100623/attachments 题意:问1到n里面有多少个数满足:本身被其各个数位加起来的和整除.例如120 % 3 == 0,111 % 3 ...