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];
    }
}
posted @ 2020-12-01 08:40  maoguai  阅读(175)  评论(0编辑  收藏  举报