题目:给定一个字符串,输出没有重复字符的最长子字符串的长度。
例:Input: "abcabcbb" Output: 3 Explanation: The answer is "abc"
, with the length of 3.
Input: "bbbbb" Output: 1 Explanation: The answer is "b"
, with the length of 1.
Input: "pwwkew" Output: 3 Explanation: The answer is "wke"
, with the length of 3.
需要注意: 考虑字符串长度为0的情况
思路:
首先将字符串转化成list,写一个 从list不同索引开始 获取没有重复子字符串的递归函数getSubstring。计算所有的不重复子串的长度,存入列表list_len里,最后取出list_len里最大的元素值,即为最长子字符串的长度。
getSubstring的实现方法为,将start位置的字符放入子串,接着看它的下一个位置是否存在与子串中,如果不存在就加入子串,如果存在,子串结束,返回重复字符位置。那么下一个子串是从什么位置开始的呢?例“abcabcbb”,寻找第一个子串,start=0,索引i=3的‘a’与i=1的‘a’重复,子串停止增加,第一个子串是“abc”,下一个子串是“bca”,……所以下一子串的start位置是,与当前子串“abc”中重复字符“a”的下一位置,即下一子串的start=1。
代码如下,但是此方法太过繁琐,时间复杂度和空间复杂度都很高。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
def getSubstring(start):
substr=ls[start]
len_ls=len(ls)
for i in range(start+1,len_ls):
if ls[i] not in substr:
substr+=ls[i]
else:
next_start=ls[start:i].index(ls[i])+1+start
return i,next_start
return len_ls,-1
ls=list(s)
len_ls=len(ls)
if len_ls>0:
list_len=[]
start=0
end,next_start=getSubstring(start)
while(end<len(ls)):
list_len.append(end-start)
start=next_start
end,next_start=getSubstring(start)
list_len.append(end-start)
return max(list_len)
else:
return 0
有没有更简单的方法呢,再看讨论贴的时候,发现了时间复杂度为O(N)的方法,真是厉害了,代码居然如此简洁。其中b是不重复子字符串的起始位置,m是之前步骤中子字符串的最大长度,d字典。
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
#b: begin m: max length in previous step d: dictionary
b, m, d = 0, 0, {}
for i, char in enumerate(s):
b, m, d[char] = max(b, d.get(char, -1) + 1), max(m, i - b), i
#多重赋值操作的等号右边表达式会在赋值操作之前全部进行解析,所以右侧表达式用到的b,d,m均
是上一次循环的结果
#等价于m=max(m, i - b),b=max(b, d.get(l, -1) + 1),d[l]= i
return max(m, len(s) - b)
当Input: "pwwkew",代码的每次循环运行结果如下:
此方法是利用 for (i,char) in enumerate(s) 同时使用下标和元素直接对字符串进行循环,每次循环将字符char作为key,对应下标作为value存入字典d中,如果存在重复字符,字典中的value不断更新。如果下一次循环到的字符char不存在与字典中,则当前子字符串长度i-b继续增加,起始位置b不变;如果下一次循环到的字符char存在与字典中,则当前子字符串结束,起始位置b变成下一子字符串的起始位置(当前子字符串中重复字符的下一位置,d.get(char,-1) + 1,表示返回指定键的值,如果值不在字典中返回-1)。此方法的优势是:没有将字符串转成list,而是直接循环;使用字典存储字符和位置,而不是list,巧妙利用了重复字符index会更新;没有涉及具体生成的字符串是什么,只关注起始位置,通过一次循环寻找结束位置,并不断更新长度最大值。