题目1:
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
思路一:这里我是有两个思路的,第一次写的时候想着直接暴力列出所有的子字符串,应该也可以通过,但是事实证明会超时,主要是时间复杂度太高了,首先暴力列出所有的子字符串都已经有了O(N^2)的时间复杂度,后面判断是否含有重复还要花费O(N)的复杂度,不通过也很正常,不过这里还是把代码贴过来,用来学习找到所有的子字符串又是有点帮助的。
代码1:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
超时
if s=="":return 0
result=[]
LengthList=[]
#x+1表示子串的长度,下面的i是依次滑动来切字符串
for x in range(len(s)):
for i in range(len(s)-x):
result.insert(0,s[i:i+x+1])
def isrep(s):
a=set(s)
if len(a)==len(s):return False#没有重复
return True#有重复
for item in result:
if not isrep(item):
return len(item)
break
思路二:真正的思路还是要想方法降低时间复杂度,这里采用双指针加上哈希表来降低复杂度,循环完整个字符串。首先ij肯定都是在字符串的开头的,然后j开始遍历整个字符串,将遍历过的s[j]存入到哈希表中,对应的值是当前访问j的索引。如果下次又碰到了相同的字符串,那就可以在哈希表中检测到它,将i指针的位置修改为哈希表中的上一次s[j]的索引,这样就能确保i+1到j(当前的索引位置)中间是没有重复元素的,每次还将res(存储结果的值)和当前的j-i相比,最后得到最大长度。
代码中也有注释
代码2:
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
#上面一种方法的时间复杂度太高,运行超时,想办法降低时间复杂度,双指针加哈希表,判断是否重复并即使更新左指针
#我们可以使用哈希表来记录上一次访问该字符的索引,通过判断哈希表中是否有该字符来更新左指针的位置
#不断的更新左指针i,因为如果一个字符出现了第二次,i就要换为最新的索引值,这样才能确保i+1到j的范围内是没有重复字符的
#然后不断的更新res结果,res和j-i一直不同的比较
#i的初值设为-1,不影响j-i的值
dic,i,res={},-1,0
#通过j遍历循环字符串
for j in range(len(s)):
if s[j] in dic:
i=max(i,dic[s[j]])#更新左指针i值到不重复的位置
#如果不在那就正常的遍历,存值
dic[s[j]]=j
res=max(res,j-i)
return res
题目二丑数:
我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。
示例:
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
思路:加入现在知道了n之前的丑数,那如果我们想要找到n+1个丑数,必然是前面的n个丑数中的某个数乘2或3或5得到的,也可以表示为xa*2,xb*3,xc*5,abc索引的大小未知但是都是前面丑数的索引。所有的丑数肯定是前面的丑数分别乘2,3,5得到的,那就是每个数都要乘2或者3或者5,但是如果从第一个丑数都每个数乘三个这样算的话,加入我们从丑数1开始,现在要计算第7个丑数,那丑数1后面得到的乘积就是2,3,5,但是2,3,5都比6小,这就造成了混乱。所以这里我们使用三个指针a代表是乘2,b代表是乘3,c代表是乘5,也可以理解为a之前的那些丑数都已经没有了乘2的机会,但是可能有乘3或者5的机会,这样就不会造成混乱,每个数都有三个机会,用完一个机会该机会对应的指针就指向下一个数,确保不会发生混乱。
代码:
class Solution:
def nthUglyNumber(self, n: int) -> int:
#丑数n肯定是前面所有的丑数中的xa*2,xb*3,xc*5所得到的(abc是不分大小的),下一个丑数肯定是三个数的最小值
#这里采用动态规划,三个指针,初始都是从第一个丑数开始的,在n之前的数,每个数都有机会分别乘2,3,5
#但是我们还不能直接每个数从1234568就开始分别乘2,3,5,如果这样的话,丑数1分别乘这三个数
#就得到2,3,5,丑数有数又是他们三个中的最小值,这样的话就和后面真实的丑数比较的时候造成
#混乱;
#所以我们还是给每个丑数分别乘三个指针(a,b,c)的三次机会,只不过a-1之前的数表示已经没有
#机会和a相乘了,n+1个丑数是xa*2,xb*3,xc*5的最小值,如果是xa*2的话,那a对应的指针就要向
#后移一位,确保a前面的数都已经用过了乘2的机会,这样分别相乘,不分abc的大小,可以确保乘出
#来的三个数必然有一个是满足条件的,不会造成混乱
#动态规划列表dp表示当前的丑数集合,dp[n]表示第n+1个丑数
dp=[1]*n
# dp[0]=1#第一个丑数是1
a,b,c=0,0,0#指针都从第一个丑数开始
#从第二个丑数开始算
for i in range(1,n):
num1,num2,num3=dp[a]*2,dp[b]*3,dp[c]*5
dp[i]=min(num1,num2,num3)#找到下一个丑数,是这三个乘积的最小值,但是abc的大小顺序是不一定的
#找到对应的是哪个指针乘积得到的,相应的位置加一,注意这里的if 判断只能使用三个if,因为可能存在两个指针同时都得到dp[i]的情况
#为了避免重复,如果同时满足条件那就同时加1
if dp[i]==num1:a+=1
if dp[i]==num2:b+=1
if dp[i]==num3:c+=1
#返回dp[n-1]
return dp[-1]
具体的题解可以看力扣题库里面的k神解释,附带ppt