• leetcode刷题 464~


    题目464题

    我能赢吗

    在 "100 game" 这个游戏中,两名玩家轮流选择从 1 到 10 的任意整数,累计整数和,先使得累计整数和达到或超过 100 的玩家,即为胜者。

    如果我们将游戏规则改为 “玩家不能重复使用整数” 呢?

    例如,两个玩家可以轮流从公共整数池中抽取从 1 到 15 的整数(不放回),直到累计整数和 >= 100。

    给定一个整数 maxChoosableInteger (整数池中可选择的最大数)和另一个整数 desiredTotal(累计和),判断先出手的玩家是否能稳赢(假设两位玩家游戏时都表现最佳)?

    你可以假设 maxChoosableInteger 不会大于 20, desiredTotal 不会大于 300。

    示例:

    输入:
    maxChoosableInteger = 10
    desiredTotal = 11

    输出:
    false

    思路

    由于在选择中无法每次都选择最大值或者有最优解,因此需要遍历每一种可能。

    状态压缩:使用二进制的第i位的0或者1来表示i这个数字的选取与否,这样所有数字的选取状态就可以用一个数来很方便的表示。

    回溯:

    本次选择数字i达到了desiredTotal,说明当前状态下能赢,即返回true,又或者下一次输了,那么说明本次选择必赢。

    实现

    class Solution:
        def canIWin(self, maxChoosableInteger: int, desiredTotal: int) -> bool:
            if maxChoosableInteger > desiredTotal: return True
            if sum(range(maxChoosableInteger + 1)) < desiredTotal: return False
            """
            dp表示"每个"取数状态下的输赢
            例如只有1,2两个数选择,那么 (1 << 2) - 1 = 4 - 1 = 3种状态表示:
            01,10,11分别表示:已经选了1,已经选了2,已经选了1、2状态下,A的输赢情况
            并且可见这个表示所有状态的dp数组的每个状态元素的长度为maxChoosableInteger位的二进制数
            """
            dp = [None for _ in range((1 << maxChoosableInteger)-1)]
    
            def dfs(visited, desiredTotal):
                if dp[visited] is not None:
                    return dp[visited]
                for i in range(1, maxChoosableInteger+1):
                    # 当前选择的位
                    cur = 1 << (i-1)
                    # 该位没有被使用过
                    if cur & visited == 0: 
                        # 如果当前选了i已经赢了或者选了i还没赢但是后面对方选择输了,cur|visited表示进行状态的更新
                        if desiredTotal - i <= 0 or not dfs(cur|visited, desiredTotal-i):
                            dp[visited] = True
                            return True
                # 全部都赢不了
                dp[visited] = False
                return False
            return dfs(0, desiredTotal)  

    题目467题

    环绕字符串中的唯一子字符串

    把字符串 s 看作是“abcdefghijklmnopqrstuvwxyz”的无限环绕字符串,所以 s 看起来是这样的:"...zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd....". 

    现在我们有了另一个字符串 p 。你需要的是找出 s 中有多少个唯一的 p 的非空子串,尤其是当你的输入是字符串 p ,你需要输出字符串 s 中 p 的不同的非空子串的数目。 

    注意: p 仅由小写的英文字母组成,p 的大小可能超过 10000。

    思路

    此题采用动态规划:

    但动态规划的变量不是当前字符在p中索引,而是当前字符。例如“lmnlmno”,其中lmn包括在lmno中,因为最长的连续子串一定是包含了比它短的连续子串,因此只需要记录最长子字符的长度即可。

    实现

    class Solution:
        def findSubstringInWraproundString(self, p: str) -> int:
            L = len(p)
            if L == 0:
                return 0  
            dp = [0 for _ in range(26)]
            p = p[0]+ p
            for i in range(1, L+1):
                cur = ord(p[i])-97
                last = ord(p[i-1])-97
                if (cur - last)%26==1:
                    maxLen +=1
                else:
                    maxLen = 1
                dp[cur] = max(dp[cur], maxLen)
            return sum(dp)

    题目468题

    验证ip地址

    编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址。

    如果是有效的 IPv4 地址,返回 "IPv4" ;
    如果是有效的 IPv6 地址,返回 "IPv6" ;
    如果不是上述类型的 IP 地址,返回 "Neither" 。
    IPv4 地址由十进制数和点来表示,每个地址包含 4 个十进制数,其范围为 0 - 255, 用(".")分割。比如,172.16.254.1;

    同时,IPv4 地址内的数不会以 0 开头。比如,地址 172.16.254.01 是不合法的。

    IPv6 地址由 8 组 16 进制的数字来表示,每组表示 16 比特。这些组数字通过 (":")分割。比如,  2001:0db8:85a3:0000:0000:8a2e:0370:7334 是一个有效的地址。而且,我们可以加入一些以 0 开头的数字,字母可以使用大写,也可以是小写。所以, 2001:db8:85a3:0:0:8A2E:0370:7334 也是一个有效的 IPv6 address地址 (即,忽略 0 开头,忽略大小写)。

    然而,我们不能因为某个组的值为 0,而使用一个空的组,以至于出现 (::) 的情况。 比如, 2001:0db8:85a3::8A2E:0370:7334 是无效的 IPv6 地址。

    同时,在 IPv6 地址中,多余的 0 也是不被允许的。比如, 02001:0db8:85a3:0000:0000:8a2e:0370:7334 是无效的。

    思路实现

    class Solution:
        def validIPAddress(self, IP: str) -> str:
            if "." in IP:
                ip = IP.split(".")
                if len(ip) != 4:
                    return "Neither"
                for num in ip:
                    if num.isdigit():
                        if "0" == num[0] and len(num) > 1:
                            return "Neither"
                        num = int(num)
                        if  0<= num <= 255:
                            continue
                        else:
                            return "Neither"
                    else:
                        return "Neither"
                return "IPv4"
            elif ":" in IP:
                base = [str(x) for x in range(10)] + [ chr(x) for x in range(ord('A'),ord('A')+6)] +[ chr(x) for x in range(ord('a'),ord('a')+6)]
                ip = IP.split(":")
                if len(ip) != 8:
                    return "Neither"
                for num in ip:
                    if len(num) > 4 or len(num)<1:
                        return "Neither"
                    for n in num:
                        if n not in base:
                            return "Neither"
                return "IPv6"    
            else:
                return "Neither"

    题目470题

    用rand7实现rand10

    已有方法 rand7 可生成 1 到 7 范围内的均匀随机整数,试写一个方法 rand10 生成 1 到 10 范围内的均匀随机整数。

    思路

    拒绝采样:

    实现

    class Solution:
        def rand10(self):
            """
            :rtype: int
            """
            while True:
                row = rand7()
                col = rand7()
                index = (row-1)*7 + col
                if index <= 40:
                    break
            return 1 + (index)%10

    题目473题

    火柴拼正方形

    还记得童话《卖火柴的小女孩》吗?现在,你知道小女孩有多少根火柴,请找出一种能使用所有火柴拼成一个正方形的方法。不能折断火柴,可以把火柴连接起来,并且每根火柴都要用到。

    输入为小女孩拥有火柴的数目,每根火柴用其长度表示。输出即为是否能用所有的火柴拼成正方形。

    示例 1:

    输入: [1,1,2,2,2]
    输出: true

    解释: 能拼成一个边长为2的正方形,每边两根火柴。

    思路

    深度优先遍历

    实现

    class Solution:
        def makesquare(self, nums: List[int]) -> bool:
            total = sum(nums)
            if total % 4 != 0:
                return False
            L = total//4
            N = len(nums)
            if N < 4 or max(nums) > L:
                return False
            visited = [False for _ in range(N)]
    
            def dfs(curL, index):
                if nums[index] > L:
                    return False
                temp = curL + nums[index]
                if temp > L:
                    return False
                elif temp < L:
                    visited[index] = True
                    for i in range(N):
                        if not visited[i] and dfs(temp, i):
                            return True
                    visited[index] = False
                    return False
                elif temp == L:
                    visited[index] = True
                    for i in range(N):
                        if not visited[i]:
                            if dfs(0, i):
                                return True
                            else:
                                visited[index] = False
                                return False
                    if False not in visited:
                        return True
                    else:
                        return False
    
            return dfs(0,0)

    题目474题

    一和零

    给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

    请你找出并返回 strs 的最大子集的大小,该子集中 最多 有 m 个 0 和 n 个 1 。

    如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

    示例 1:

    输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
    输出:4
    解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
    其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

    思路

    动态规划:背包九讲

    实现

    class Solution:
        def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
            L = len(strs)
            f = [[0 for _ in range(n+1)] for _ in range(m+1)]
            for str in strs:
                count0, count1 = str.count("0"), str.count("1")
                for zero in range(m, count0-1, -1):
                    for one in range(n, count1-1, -1):
                        f[zero][one] = max(1+f[zero - count0][one - count1],f[zero][one])            
            return f[m][n]

    题目475题

    供暖器

    冬季已经来临。 你的任务是设计一个有固定加热半径的供暖器向所有房屋供暖。

    在加热器的加热半径范围内的每个房屋都可以获得供暖。

    现在,给出位于一条水平线上的房屋 houses 和供暖器 heaters 的位置,请你找出并返回可以覆盖所有房屋的最小加热半径。

    说明:所有供暖器都遵循你的半径标准,加热的半径也一样。

    示例 1:

    输入: houses = [1,2,3], heaters = [2]
    输出: 1
    解释: 仅在位置2上有一个供暖器。如果我们将加热半径设为1,那么所有房屋就都能得到供暖。

    思路

    思路非常简单,对于每个房间,找到其对每个供暖器的最大值,取其中的最小值就是结果。这样时间复杂度为O(mn)。为了减小时间复杂度,在选择对于每个供暖器的最大值时候可以采用二分法

    实现

    class Solution:
        def findRadius(self, houses: List[int], heaters: List[int]) -> int:
            heaters.sort()
            minL = 0
            heaterRight = list()
            for heaterIndex in range(len(heaters)-1):
                heaterRight.append((heaters[heaterIndex]+heaters[heaterIndex+1])//2)
            for house in houses:
                left, right = 0, len(heaterRight)-1
                while left < right:
                    mid = left + (right-left)//2
                    if heaterRight[mid] == house:
                        left = mid
                        break
                    elif heaterRight[mid] < house:
                        left = mid + 1
                    elif heaterRight[mid] > house:
                        right = mid -1
                if len(heaterRight) >= 1 and house > heaterRight[left]:
                    left += 1
                minL = max(abs(heaters[left]-house),minL)
            return minL

    题目476题

    数字的补码

    给定一个正整数,输出它的补数。补数是对该数的二进制表示取反。

    思路

    实现

    class Solution:
        def findComplement(self, num: int) -> int:
            result,level = 0,0
            while num:
                temp = (num & 1) ^ 1
                num = num >> 1
                temp = temp << level
                result |= temp
                level += 1
            return result

    题目477题

    汉明距离总和

    两个整数的 汉明距离 指的是这两个数字的二进制数对应位不同的数量。

    计算一个数组中,任意两个数之间汉明距离的总和。

    思路

    假设数组中的每个数都表示为 k 位的二进制数(高位补 0),那么我们可以发现,要计算数组中任意两个数的汉明距离的总和,可以先算出数组中任意两个数二进制第 i 位的汉明距离的总和,在将所有的 k 位之和相加。也就是说,二进制中的每一位都是可以独立计算的。考虑数组中每个数二进制的第 i 位,假设一共有 t 个 0 和 n - t 个 1,那么显然在第 i 位的汉明距离的总和为 t * (n - t)

    实现

    class Solution:
        def totalHammingDistance(self, nums: List[int]) -> int:
            bits = [0 for _ in range(32)]
            for num in nums:
                i = 0
                while num > 0:
                    bits[i] += (num & 1)
                    num >>= 1
                    i += 1
            N = len(nums)
            result = 0
            for i in range(32):
                result += bits[i]*(N - bits[i])
            return result

    题目478题

    在圆内随机生成点

    给定圆的半径和圆心的 x、y 坐标,写一个在圆中产生均匀随机点的函数 randPoint 。

    说明:

    输入值和输出值都将是浮点数。
    圆的半径和圆心的 x、y 坐标将作为参数传递给类的构造函数。
    圆周上的点也认为是在圆中。
    randPoint 返回一个包含随机点的x坐标和y坐标的大小为2的数组。

    思路实现

    class Solution:
    
        def __init__(self, radius: float, x_center: float, y_center: float):
            self.radius = radius
            self.x_center = x_center
            self.y_center = y_center        
    
        def randPoint(self) -> List[float]:
            radius = self.radius
            while True:
                x = random.random()
                y = random.random()
                if x**2 + y**2 <= 1:
                    break
            x_offset = random.choice((-1, 1)) * x * radius + self.x_center
            y_offset = random.choice((-1, 1)) * y * radius + self.y_center
            return [x_offset, y_offset]
    class Solution: def __init__(self, radius: float, x_center: float, y_center: float): self.radius = radius self.x_center = x_center self.y_center = y_center def randPoint(self) -> List[float]: p = self.radius * math.sqrt(random.random()) theta = random.random() * math.pi * 2 return [self.x_center + p*math.cos(theta), self.y_center + p*math.sin(theta)]

    题目481题

    神奇字符串

    神奇的字符串 S 只包含 '1' 和 '2',并遵守以下规则:

    字符串 S 是神奇的,因为串联字符 '1' 和 '2' 的连续出现次数会生成字符串 S 本身。

    字符串 S 的前几个元素如下:S = “1221121221221121122 ......”

    如果我们将 S 中连续的 1 和 2 进行分组,它将变成:

    1 22 11 2 1 22 1 22 11 2 11 22 ......

    并且每个组中 '1' 或 '2' 的出现次数分别是:

    1 2 2 1 1 2 1 2 2 1 2 2 ......

    你可以看到上面的出现次数就是 S 本身。

    给定一个整数 N 作为输入,返回神奇字符串 S 中前 N 个数字中的 '1' 的数目。

    思路实现

    class Solution:
        def magicalString(self, n: int) -> int:
            if n <=0:
                return 0
            magic = [1,2,2]
            l, i,last =3, 2, 2
            result = 1
            while l < n:
                x = magic[i]
                y = 1 if last == 2 else 2
                magic.append(y)
                if x == 2:
                    magic.append(y)
                if y == 1:
                    result += x
                i += 1
                last = y
                l += x
            if n > 3 and n < l and magic[-1] == 1:
                result -= 1
            return result

    题目482题

    密钥格式化

    有一个密钥字符串 S ,只包含字母,数字以及 '-'(破折号)。其中, N 个 '-' 将字符串分成了 N+1 组。

    给你一个数字 K,请你重新格式化字符串,使每个分组恰好包含 K 个字符。特别地,第一个分组包含的字符个数必须小于等于 K,但至少要包含 1 个字符。两个分组之间需要用 '-'(破折号)隔开,并且将所有的小写字母转换为大写字母。

    给定非空字符串 S 和数字 K,按照上面描述的规则进行格式化。

    思路实现

    class Solution:
        def licenseKeyFormatting(self, S: str, K: int) -> str:
            s = S.replace('-','').upper()
            result, k = "", 0
            for key in s[::-1]:
                result = key + result
                k += 1
                if k == K:
                    result = "-" + result
                    k = 0
            if result and result[0] == "-":
                return result[1:]
            return result

    题目485题

    最大连续1的个数

    给定一个二进制数组, 计算其中最大连续1的个数。

    思路实现

    class Solution:
        def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
            result = temp = 0
            for num in nums:
                if num == 1:
                    temp += 1
                else:
                    result = max(result,temp)
                    temp = 0
            return max(result,temp)

    题目486题

    预测赢家

    给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。

    给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。

    示例 1:

    输入:[1, 5, 2]
    输出:False
    解释:一开始,玩家1可以从1和2中进行选择。
    如果他选择 2(或者 1 ),那么玩家 2 可以从 1(或者 2 )和 5 中进行选择。如果玩家 2 选择了 5 ,那么玩家 1 则只剩下 1(或者 2 )可选。
    所以,玩家 1 的最终分数为 1 + 2 = 3,而玩家 2 为 5 。
    因此,玩家 1 永远不会成为赢家,返回 False 。

    思路

    1.递归

    2.动态规划:

    dp[i][j] 表示当数组剩下的部分为下标 i到下标 j时,当前玩家与另一个玩家的分数之差的最大值,注意当前玩家不一定是先手。

    dp[i][j]=max(nums[i]dp[i+1][j],nums[j]dp[i][j1])

    实现

    class Solution:
        def PredictTheWinner(self, nums: List[int]) -> bool:
            L = len(nums)%2
    
            def get(one,two,left, right):
                if left == right:
                    if L == 1:
                        return True if one + nums[left] >= two else False
                    else:
                        return True if one + nums[left] > two else False
                one1 = one + nums[left]
                one2 = one + nums[right]
                if (get(two,one1,left+1,right) and get(two,one2,left,right-1)) is False:
                    return True
                return False
            
            return get(0,0,0,len(nums)-1) 
    
    class Solution:
        def PredictTheWinner(self, nums: List[int]) -> bool:
            L = len(nums)
            dp = [[0 for _ in range(L)] for _ in range(L)]
            for i in range(L):
                dp[i][i] = nums[i]
            for i in range(L-2,-1,-1):
                for j in range(i+1,L):
                    dp[i][j] = max(nums[i] - dp[i+1][j], nums[j]- dp[i][j-1])
            return dp[0][L-1] >= 0
    
    class Solution:
        def PredictTheWinner(self, nums: List[int]) -> bool:
            L = len(nums)
            dp = [0 for _ in range(L)]
            for i in range(L):
                dp[i] = nums[i]
            for i in range(L-2,-1,-1):
                for j in range(i+1,L):
                    dp[j] = max(nums[i] - dp[j], nums[j]- dp[j-1])
            return dp[L-1] >= 0

    题目491题

    递增子序列

    给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。

    示例:

    输入: [4, 6, 7, 7]
    输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]

    思路

    递归

    实现

    class Solution {
        private List<List<Integer>> res = new ArrayList<List<Integer>>();
        private List<Integer> temp = new ArrayList<>();
        private int[] nums;
        public List<List<Integer>> findSubsequences(int[] nums) {
            this.nums = nums;
            dfs(0,Integer.MIN_VALUE);
            return res;        
        }
        private void dfs(int curIndex, int preValue){
            if (curIndex == nums.length){
                if (temp.size() > 1){
                    res.add(new ArrayList<>(temp));
                }
                return;
            }
            if (nums[curIndex] >= preValue){
                temp.add(nums[curIndex]);
                dfs(curIndex+1, nums[curIndex]);
                temp.remove(temp.size()-1);
            }
            if (nums[curIndex] != preValue){
                dfs(curIndex+1, preValue);
            }
        }
    }
    
    class Solution:
        def findSubsequences(self, nums: List[int]) -> List[List[int]]:
            L = len(nums)
            res = list()
            for i in nums:
                temp = []
                for j in res:
                    if i >= j[-1]:
                        temp.append(j+[i])
                res.append([i])
                for t in temp:
                    if t not in res:
                        res.append(t)
            result = []
            for i in res:
                if len(i) > 1:
                    result.append(i)
            return result

    题目492题

    构造矩形

    作为一位web开发者, 懂得怎样去规划一个页面的尺寸是很重要的。 现给定一个具体的矩形页面面积,你的任务是设计一个长度为 L 和宽度为 W 且满足以下要求的矩形的页面。

    思路实现

    class Solution {
        public int[] constructRectangle(int area) {
            int[] result = new int[2];
            int W = (int)Math.floor(Math.sqrt(area));
            while(area % W != 0){
                W -= 1;
            }
            result[0] = area/W;
            result[1] = W;
            return result;
        }
    }

    题目494题

    目标和

    给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。

    返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

    思路

    1.递归

    2.3. 动态规划 dp[i][j]表示运行到nums[i]时,其取值 = j 的个数

    实现

    1.
    class Solution {
        private int result = 0;
        private int S;
        private int[] nums;
        public int findTargetSumWays(int[] nums, int S) {
            this.S = S;
            this.nums = nums;
            dfs(0,0);
            return result;
        }
        private void dfs(int curIndex, int Value){
            if (curIndex == nums.length){
                if (Value == S){
                    result += 1;
                }
                return;
            }
            dfs(curIndex+1, Value + nums[curIndex]);
            dfs(curIndex+1, Value - nums[curIndex]);
        }
    }
    
    2.
    class Solution {
        public int findTargetSumWays(int[] nums, int S) {
            int numsLen = nums.length;
            int sum = 0;
            int s = Math.abs(S);
            for (int num : nums) sum += num;
            if(s > sum){
                return 0;
            }
            int[][] dp = new int [numsLen][sum+1];
            dp[0][nums[0]] = (nums[0] ==0 ? 2 : 1);
    
            for(int idx=1; idx < numsLen; idx++){
                for(int j= 0; j <= sum; j++ ){
                    int num1 = Math.abs(j-nums[idx]);
                    int num2 = Math.abs(j+nums[idx]);
                    dp[idx][j] += (num1 <= sum ? dp[idx-1][num1]:0);
                    dp[idx][j] += (num2 <= sum ? dp[idx-1][num2]:0);
                }
            }
            return dp[numsLen-1][s];
        }
    }
    
    3.
    class Solution {
        public int findTargetSumWays(int[] nums, int S) {
            int numsLen = nums.length;
            int sum = 0;
            int s = Math.abs(S);
            for (int num : nums) sum += num;
            if(s > sum){
                return 0;
            }
            int[] dp = new int [sum+1];
            dp[nums[0]] = (nums[0] ==0 ? 2 : 1);
            for(int idx=1; idx < numsLen; idx++){
                int[] next = new int [sum+1];
                for(int j= 0; j <= sum; j++ ){
                    int num1 = Math.abs(j-nums[idx]);
                    int num2 = Math.abs(j+nums[idx]);
                    next[j] += (num1 <= sum ? dp[num1]:0);
                    next[j] += (num2 <= sum ? dp[num2]:0);
                }
                dp = next;
            }
            return dp[s];
        }
    }
  • 相关阅读:
    USART串行通信
    GPIO
    adb工具获取andriod设备日志
    [从今天开始修炼数据结构]线性索引查找
    [从今天开始修炼数据结构]有序表查找
    [从今天开始修炼数据结构]查找算法概论和顺序表查找
    [边缘计算]挑战与愿景
    [从今天开始修炼数据结构]无环图的应用 —— 拓扑排序和关键路径算法
    [从今天开始修炼数据结构]图的最短路径 —— 迪杰斯特拉算法和弗洛伊德算法的详解与Java实现
    [从今天开始修炼数据结构]图的最小生成树 —— 最清楚易懂的Prim算法和kruskal算法讲解和实现
  • 原文地址:https://www.cnblogs.com/mgdzy/p/14066102.html
Copyright © 2020-2023  润新知