上一期有留一个小bug让小伙伴们找,不知道多少人自己找到了啊?爱学习的人肯定自己去尝试了,肯定发现leetcode上运行结果发现输出不是预期的[7, 0, 8],而是像下边这样:
Finished in 36 ms [7, 0.6999999999999993, 8.07, 1]
一个不合预期的地方是出现了小数,还有一个则是链表长度不合预期。其实,这个是除法导致的,这里的除法保留了小数部分,导致进位标志carry不是我们需要的整型0或者1了,所以出现了小数,另一方面进位的错误也导致在最高位的时候再次进了一位,即链表中多出了个1。修改方法很简单,只需要在两处carry位置进行类型转换,具体如下。或者注意''/''和“//”的区别,后者所除的结果仅保留商(整型),前者即存在小数。
carry = int(p.next.val / 10) #int()强制转换为整型
上期没找出原因的小伙伴可以去改过来试试看噢~
No.3 Longest Substring without Repeating Characters
原题:
Given a string, find the length of the longest substring without repeating characters.
题目大意:给出一个字符串,找到最长的没有重复字符的子字符串,并返回该子字符串的长度。
例如:
Given "abcabcbb", the answer is "abc", which the length is 3. Given "bbbbb", the answer is "b", with the length of 1. Given "pwwkew", 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.
可能是前边的题目都大同小异,难度也接近。也有可能是人的思维有惯性,小詹又是利用循环嵌套遍历所有情况进行判断的,这种简单粗暴可以实现,但是效率很低。大体思路是:第一层循环从字符串的最左侧到最右侧第二个,即for i in range(0,len(s)-1),第二层循环则从第一层紧跟着的一个到最后一个字符。即for j in range(i+1,len(s));之后通过找出所有不重复的子字符串,比较长度得到最大长度的子字符串。代码如下:(需要注意当字符串长度为0或1的特殊情况)
def lengthOfLongestSubstring(self, s): """ :type s: str :rtype: int """ max_len = 0 #用这个值记录我们要返回的最长子字符串长度 #当原字符串长度为0或1的特殊情况 if (len(s) == 1 or len(s) == 0): max_len = len(s) #开始遍历每一个子字符串,并进行长度比较,得到最长的那个 for i in range(0,len(s)-1): for j in range(i+1, len(s)): if s[j] in s[i:j]: if j-i > max_len: right = j left = i #这里小詹本想返回对应子字符串的左右索引值,之后发现题目没有要求 max_len = right-left break elif j == len(s) - 1: if max_len < j - i + 1: max_len = j - i + 1 return max_len
结果当然是可以通过的啦,然而时间效率方面很差很差,如图:
然而,我们不能满足于这种最低效率的实现结果。下边提出一个炒鸡牛逼的方法,非原创,小詹花了很久才搞明白其思路,其利用到了字典的方法。什么是字典?请自行补充知识噢(公众号有语法综述)。先放代码后再解释:
def lengthOfLongestSubstring(self, s): """ :type s: str :rtype: int """ #创建一个空字典,其存放的形式是“单字符:出现位置的索引” indexDict = {} #存放记录最大长度和当前循环下的长度 maxLength = currMax = 0 for i in range(len(s)): #这里是关键,小詹看了挺久的,小伙伴们比我强,应该比较快 #这里是当s[i]没有在之前出现过,则当前长度currMax自动加一 #当出现了重复字符,则比较当前找到的子字符串长度和历史最大长度 #重点是这里i - indexDict[s[i]] - 1 的含义;代码后举例具体讲解 if s[i] in indexDict and i - indexDict[s[i]] - 1 <= currMax: if maxLength < currMax: maxLength = currMax currMax = i - indexDict[s[i]] - 1 currMax = currMax + 1 indexDict[s[i]] = i return maxLength if currMax < maxLength else currMax
代码里对应位置加入了注释,理解起来应该好很多了,这里举例说明下为什么【i - indexDict[s[i]] - 1】代表了当前找到子字符串的长度。
比如字符串'abcdadd',代码运行过程中一直迭代到i=3【对应字符d】时,都不满足s[i] in indexDict ,不执行条件语句,而是currMax依次加一,并且将字符信息以{s[i]:i}的形式存放在字典中。当继续迭代i=4时,进入条件语句,这里主要解释【i - indexDict[s[i]] - 1】,检测到了重复字符'a',之前该字符出现位置为i=0处即【indexDict[s[i]] =0】这时候当前检测到的无重复字符子串为'abcd',长度为【4-indexDict[s[i]] -1 = 3】。其他同此例。