无重复字符的最长子串

无重复字符的最长子串
  • 我的思路:想要每次一遇到重复字符,就把与之重复的字符之前的全部切掉,然后在后面的字符串里继续寻找一个无重复子串,然后与前面找到的一个进行比较。最终,找出最长子串。然而,实际运行并不是我想象的那样,因为for循环中的i与length都已经不能被改变,我以为随着我对s的切片,在for循环中length就随之改变,且i也能重新开始轮回…(下面的程序还不正确)
def lesngthOfLongestSubstring(s: str) -> int:
	s = list(s)
    temp, result = [], []

    length = len(s)
    for i in range(length):   # i, len(s)都无法再被改变了!
        # print(s)            # 验证了切片语句未起作用...
        if s[i] not in temp:
            temp.append(s[i])

        else:
            if len(temp) > len(result):
                result = temp
                temp.clear()
                temp.append(s[i])
            #s[s.index(s[i]):] # 这条语句不会起作用...
            #length = len(s)   # 这条语句不会起作用...
            #i = 0             # 这条语句不会起作用...
            
    return len(result)

print(lesngthOfLongestSubstring("pwwkew"))
  • 一遍遍修改程序时,总能发现特殊情况没有考虑到(因为在运行时出现了与预期结果不一致的情况,所以才发现了特殊情况),然后一遍遍的进行bug的修补。

  • 好吧,测试用例中总有我无法通过的,我还是放弃自己写吧…如下面的程序,当测试到"dvdf"时又出现了错误。。。(下面的程序还不正确)

def lesngthOfLongestSubstring(s: str) -> int:
    if len(s) == 0:
        return 0
    if len(s) == 1:
        return 1
    
    s = list(s)
    temp, result = [], []
    
    length = len(s)
    for i in range(length): # i, len(s)都无法再被改变了!
    if s[i] not in temp:
        temp.append(s[i])
        if i == length - 1 and len(temp) > len(result):
            result = temp
    else:
        if len(temp) > len(result):
            result.clear()
            for i in range(len(temp)):
                result.append(temp[i])
            #result = temp # 不能使用这种赋值方式,否则如果找到最长子串后,由改变了temp,那么result也就变了!如“cdd”,最后结果是1,而不是2了!
            temp.clear()
            temp.append(s[i])

    return len(result)

print(lesngthOfLongestSubstring("dvdf"))

在这里插入图片描述

  • 下面是参考其他人的答案进行的修正。

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

暴力法

  • 思路:逐个检查所有的子字符串,看它是否不含有重复的字符。一旦遇到重复的字符串,就检查下一子字符串。每计算出一个子字符串的长度后,就与前一个相比较,最终得到最大的无重复子字符串长度。

  • 程序:

def lengthOfLongestSubstring(s):
'''
得到最长子字符串的长度
'''
	n = len(s)
	ans = 0
	for i in range(n):
		for j in range(i+1, n+1):
			if allUnique(s, i, j):
				ans = max(ans, j-i)
	return ans

def allUnique(s, start, end):
'''
判断每个子字符串是否有重复的字符。如果有,则返回False,从而不必参与max(ans, j-i)比较
'''
	set_ = set()
	for i in range(start, end):
		ch = s[i]
		if ch in set_:
			return False
		set_.add(ch)
	return True

print(lengthOfLongestSubstring("dvdf"))
  • 运行过程:
    对于测试字符串dvdf来说,此程序将分别遍历子字符串d, dv, dvd, dvdf, v, vd, vdf, d, df, f,最终,得到最长子字符串长度为3
  • 运行结果:
    在这里插入图片描述
  • 算法思想:
  1. 为了枚举给定字符串的所有子字符串,我们需要枚举它们开始和结束的索引。假设开始和结束的索引分别为ij。那么0 <= i <= j <= len(s)。因此,使用i 0 0 0 n − 1 n - 1 n1以及j从 i + 1 +1 +1 n n n这两个嵌套的循环,我们可以枚举出字符串s的所有子字符串。
  2. 检查一个字符串是否有重复字符,我们可以使用集合。我们遍历字符串中的所有字符,并将它们逐个放入集合set_中。在放置一个字符之前,我们检查该集合是否已经包含它。如果包含,我们会返回 False。否则,我们就可以返回true
  • 复杂度分析:
    在这里插入图片描述

滑动窗口

说明:

  • 暴力法非常简单,但它太慢了。
  • 滑动窗口是数组/字符串问题中常用的抽象概念。 窗口通常是在数组/字符串中由开始和结束索引定义的一系列元素的集合,即 [ i , j ) [i, j) [i,j)(左闭,右开)。而滑动窗口是可以将两个边界向某一方向“滑动”的窗口。例如,我们将 [ i , j ) [i, j) [i,j)向右滑动1个元素,则它将变为 [ i + 1 , j + 1 ) [i+1, j+1) [i+1,j+1)(左闭,右开)。

思路

  1. 我们使用列表类型的变量l存储当前窗口 [ i , j ) [i, j) [i,j)(最初 j = i j = i j=i)中;

注意:参考文章说的是使用HashSet类型的变量,因为他使用的是Java语言实现。而我使用Python语言实现,如果使用集合类型,在pop()操作时会出现不期望的结果,因为插入到集合中的元素的实际存储并不按照插入顺序。所以在这里我选择了集合类型变量。况且,Python中有in运算,实际上除此之外,选用元组这一有序序列也是可以的

  1. 然后我们向右侧滑动索引 j j j
  • 如果它不在列表l中,我们会继续滑动 j j j,直到 s[j] 已经存在于 l 中。【此时,我们找到的没有重复字符的最长子字符串就是长度为 ( j + 1 − i ) (j+1-i) (j+1i) 的以 s[i] 开头、以s[j]结尾的字符串。】
  • 若s[j] 已经存在于 l 中,就对列表l进行pop()操作,同时改变 i i i,使 i i i向后滑动,即,令i += 1
  1. 循环步骤2,直至 i i i j j j 不小于len(s)

程序:

def lengthOfLongestSubstring(s):
	n = len(s)
	l = list()
	ans, i, j = 0, 0, 0
	while i < n and j < n:
		#print(s[j])
		if s[j] not in l:
			l.append(s[j])
			j += 1
			#print("j", j)
			ans = max(ans, j - i)
		else:
			l.pop(0)
			#print("i", i)
			i += 1
	return ans

print(lengthOfLongestSubstring("dvdf"))

在这里插入图片描述

  • 复杂度分析:
    在这里插入图片描述

优化的滑动窗口

思路:
一旦遇到字符s[j]与列表l中的字符有重复时,我们不需要逐渐增加 i i i ,而是直接跳过 l[0] ~ 与s[j]相同的字符所在的位置 之间的所有字符,并将 i i i 变为 与s[j]相同字符所在的位置的索引 + 1

实现:
使用字典类型的变量,存储 不重复子字符串的每个字符及其在s中的索引 之间的映射关系。一旦遇到重复字符,就可以通过字典中保存的映射关系找出这个重复字符在s中的索引 x x x,然后 i i i 直接变为 ( x + 1 ) (x+1) (x+1)

程序:

def lengthOfLongestSubstring(s):
	n = len(s)
	d = dict()
	ans, i = 0, 0
	for j in range(0, n):
		if s[j] in d:
			i = max(d.get(s[j])+1, i)
		ans = max(ans, j+1-i)
		d[s[j]] = j
	return ans

print(lengthOfLongestSubstring("abcdc"))

在这里插入图片描述

  • 复杂度分析:
    在这里插入图片描述
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值