剑指offer--最长不含重复字符的子字符串and丑数问题

题目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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值