码迷,mamicode.com
首页 > 其他好文 > 详细

#3 Longest Substring Without Repeating Characters

时间:2015-07-08 10:49:34      阅读:125      评论:0      收藏:0      [点我收藏+]

标签:

一、题目

Given a string, find the length of the longest substring without repeating characters. For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. For "bbbbb" the longest substring is "b", with the length of 1.

 

二、解析

求最大不重复子序列的长度,只需认清一个事实:若新来字符与已有自序列中的字符重复,则自序列该重复字符之前(包括自身)都不可用,只保留之后的。

例如abcbe,这里b是重复的,所以第一个b之前(包括自身)所组成的字串都是重复的,如abcb, bcb。所以要删除ab,保留cbe,继续向下走。

 

三、实现思路

1、用串+hash实现,通过字符串操作实现解析中步骤。将目前得到的最长字串存到哈希表中。144ms

2、用串实现,通过字符串操作实现解析中步骤,用maxlen记录目前最长字串长度。104ms

3、用hash实现,通过哈希操作实现解析中步骤,同时保留串做辅助。500ms

 

四、难点

  串的难点,会了之后好像没什么难点。主要还是思路上吧,最开始思路有点跑偏,将重复字符分成两种情况:1.与当前串第一个字符相同,就用类似队列的方式把第一个挤出去。2.与当前串其中一个字符相同,就截掉当前串之前的,从重复处开始:比如dvdf,截掉后为df。但是这样明显不对,最长应该是vdf才对。

  与同学讨论后发现,我这样分没错,的确只有这两种情况,但是错在对第2中问题的处理方法不够全面,截断的不对。所以还是按照解析中的来,可以包括所有情况。

 

五、Python代码(三个版本)

1、list+hash

class Solution:
    # @param {string} s
    # @return {integer}
    
    _dict = {}
    def createHashTable(self, longestSub):
        global _dict
        value = longestSub
        key = len(value)
        isExist = _dict.get(key, no)
        if isExist == no:
            _dict[key] = value
        else:
            pass
        
    def lengthOfLongestSubstring(self, s):
        global _dict
        longestSub = ""
        length = len(s)
        _dict = {}
        for cur in range(length):
       #检查当前要元素s[cur]是否已经在当前最长自序列longestSub里
       #如果不在,则说当前元素是新的,直接加进来。之所以建立哈希表是为了存储当前最长值,为{len:longestSub}
       if s[cur] not in longestSub: longestSub += s[cur] self.createHashTable(longestSub)
       #如果在,就按照解析中做法,删除重复元素之前的元素,加上当前元素后组成longestSub。
#这里不用哈希操作是因为这里是减字符,肯定少,没必要存。
else: pos = longestSub.index(s[cur]) longestSub = longestSub[pos+1:] + s[cur]
#字典中key为子串的长度,得到长度,得到最大,返回即可。 checkMaxList
= _dict.keys() if len(checkMaxList) == 0: return 0 else: longestInt = max(checkMaxList) return longestInt

   这种方法运行时间为144ms,在Python排名中排中间靠后。后来想了一下,完全没必要用哈希去存当前最大,本题只要返回最大长度即可,用一个变量就可以搞定。至于为什么最开始会用哈希,是因为看了一眼题目的tag,里面有hash......第二种方法是纯list实现

2、list

 1 class Solution:
 2     # @param {string} s
 3     # @return {integer}
 4     def lengthOfLongestSubstring(self, s):
 5         longestSub = ""
 6         length = len(s)
 7         maxlen = 0
 8         _dict = {}
 9         for cur in range(length):
10             if s[cur] not in longestSub:
11                 longestSub += s[cur]
12                 len_sub = len(longestSub)
13                 if len_sub > maxlen:
14                     maxlen = len_sub
15                 else:
16                     pass
17             else:
18                 pos = longestSub.index(s[cur]) 
19                 longestSub = longestSub[pos + 1 :] + s[cur]
20                 
21         return maxlen

这样就清爽多了,执行时间104ms,在Python中排在前10%,还能怎么快呢?

当前时间复杂度应该是O(n^2),第一反应是优化成O(nlogn),但这样就得有二分查找的过程。但本题中子串并不是按顺序排列的,怎么优化呢?感觉这条路走不通。

分析了下程序,感觉开销应该在查找longestSub中是否存在重复字符上,即if s[cur] in longestSub,是遍历的所以比较慢。于是我将这一步换成了在哈希中查找,那查找的复杂度不就是O(1)了吗?

3、Hash

 1 class Solution:
 2     # @param {string} s
 3     # @return {integer}
 4     def lengthOfLongestSubstring(self, s):
 5         longestSub = ""
 6         length = len(s)
 7         maxlen = 0
 8         _dict = {}
 9         for cur in range(length):
10             #check wheather the current character already exist in the dict
11             pos = _dict.get(s[cur], no)
12             #用字典方法查找,复杂度为O(1)13             if pos == "no":
14                 longestSub += s[cur]
15                 _dict[s[cur]] = longestSub.index(s[cur])
16                 len_dict = len(_dict)
17                 if len_dict > maxlen:
18                     maxlen = len_dict
19                 else:
20                     pass
21             #in, cut off the previous substring, and change the value by -
22             else:
#tempString为重复字符之前的子串,需要被删除的。用tempString做索引,删除dict中相应字符
23 tempString = longestSub[: pos + 1] 24 longestSub = longestSub[pos + 1 :] + s[cur] 25 #del previous character in dict. 26 for i in tempString: 27 _dict.pop(i) 28 for i in longestSub[:-1]: 29 _dict[i] = _dict[i] - pos - 1
#新来的重复字符,由于已经删掉之前重复,现在这个就变得不重复了,放在最后,用str中的下标作为value 30 _dict[s[cur]] = longestSub.index(s[cur]) 31 32 return maxlen

这个方法执行时间为508ms.....在排名上已经靠后的找不着了。为什么会这么慢呢,已经解决查找的问题了呀。分析决定慢在这里:

1 for i in tempString:
2     _dict.pop(i)
3 for i in longestSub[:-1]:
4     _dict[i] = _dict[i] - pos - 1

即要遍历hash表,去删除重复子串,并且需要挨个修改不重复子串的下标。好吧,顾的上这一头,就顾不上那一头。。为什么需要下标呢?

  因为哈希表的建立是用longestSub<string>来做索引,比如"abc",在哈希中表示为{‘a‘:0, ‘b‘:1, ‘c‘:2}。这样当有重复时,比如abcb,先得到重复字符b在哈希中的value值为1,这一步完成了longestSub中的查找,复杂度为O(1),解决了方法二中的问题。得到后pos为1后,要删除的是"ab",保留的是"cd"。然后就在哈希中pop(a,b),修改哈希中c的value为2-1-1=0,并加上b,为1,即哈希变成{‘c‘:0,‘b‘:1}。这样一来,哈希和longestSub就时刻对应,相互依存。

  如何解决哈希变更value的问题,使得真正发挥哈希的效果呢?看看discuss找找启发再看吧。

 

六、总结

1、知道了LeetCode中定义子函数、使用全局变量的方法

2、当自己被套到圈子里出不来时,与别人讨论是极好的,好的方法是讨论出来的,总是能查漏补缺,共同进步。

3、有优化算法的冲动,要去看看到底如何做到那么快。

 

#3 Longest Substring Without Repeating Characters

标签:

原文地址:http://www.cnblogs.com/breada/p/4627675.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!