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][j−1])
实现
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]; } }