leetcode第149场周赛

leetcode第149场周赛

  • 比赛感想
  • 1154. 一年中的第几天
    • 题目大意
    • 解题思路
    • 相关代码
  • 1155. 掷骰子的N种方法
    • 题目大意
    • 解题思路
    • 相关代码
  • 1156. 单字符重复子串的最大长度
    • 题目大意
    • 解题思路
    • 相关代码
  • 1157. 子数组中占绝大多数的元素
    • 题目大意
    • 解题思路
    • 相关代码

比赛感想

做出来了三题,最后一题python查询用O(n)的方法超时了,一直在想小于O(n)的方法,然后gg,不过别人用c++写的O(n)的方法也很巧妙,学习到了,另外python用collections.Counter就能过,估计这个库的优化很好吧。

1154. 一年中的第几天

题目大意

给你一个按 YYYY-MM-DD 格式表示日期的字符串 date,请你计算并返回该日期是当年的第几天。

通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 2 天,依此类推。每个月的天数与现行公元纪年法(格里高利历)一致。

解题思路

比较简单的一道题,你只要知道每个月份都有多少天,并且闰年的二月为29天(闰年为400的倍数 或者 4的倍数且不为100的倍数),当前日期的天数即为(MM - 1)个月的天数总和 + DD

相关代码

class Solution:
    def ordinalOfDate(self, date: str) -> int:
    	# 生成一个月份天数表,也可以自己手打一个
        month_days = []
        for i in range(12):
            if i == 1:
                month_days.append(28)
            elif i % 2 == 0:
                if i <= 6:
                    month_days.append(31)
                else:
                    month_days.append(30)
            elif i % 2 == 1:
                if i <= 6:
                    month_days.append(30)
                else:
                    month_days.append(31)

        year = int(date[0:4])
        month = int(date[5:7])
        day = int(date[8:])
        ans = 0
        for i in range(1, month):
            ans += month_days[i - 1]
            if i == 2 and (year % 400 == 0 or (year % 4 == 0 and year % 100 != 0)):
                ans += 1
        ans += day
        return ans

1155. 掷骰子的N种方法

题目大意

这里有 d 个一样的骰子,每个骰子上都有 f 个面,分别标号为 1, 2, …, f。

我们约定:掷骰子的得到总点数为各骰子面朝上的数字的总和。

如果需要掷出的总点数为 target,请你计算出有多少种不同的组合情况(所有的组合情况总共有 f^d 种),模 10^9 + 7 后返回。

解题思路

动态规划,用ans[i][j]表示用了i颗骰子,掷出j的组合情况总数。
ans[0][0] = 1,
ans[i][j] = ans[i-1][j-1] + ans[i-1][j-2] + … + ans[i -1][j - f],
最后输出ans[d][target] 即可。
由于每一层只和上一层有关,可以稍加空间优化,ans[j] = ans[j - 1] + … + ans[j - f],j的状态从大到小遍历即可,最后输出ans[target],记得除以模取余哦。

相关代码

class Solution:
    def numRollsToTarget(self, d: int, f: int, target: int) -> int:
        ans = [0 for j in range(target + 1)]
        ans[0] = 1
        for i in range(d):
            for j in range(target , -1, -1):
                ans[j] = 0
                for k in range(1, f + 1):
                    if j - k >= 0 and ans[j - k] != 0:
                        ans[j] = (ans[j] + ans[j - k]) % (10**9 + 7)
        return ans[target]

1156. 单字符重复子串的最大长度

题目大意

如果字符串中的所有字符都相同,那么这个字符串是单字符重复的字符串。

给你一个字符串 text,你只能交换其中两个字符一次或者什么都不做,然后得到一些单字符重复的子串。返回其中最长的子串的长度。

解题思路

这题题意很清楚,要么只能换一个字符,要么不换,故我们每次都记录三段,上上段一样的,上段一样的,以及当前一样的段。
若上上段的内容和当前段的内容一致,且上段的字符只有1个,那么我们可以把上段换掉,让上上段和当前段拼接起来,如果别的地方还有当前段的字符则可以用其它地方的字符去替换,如果没有,则只能用上上段最左侧或当前段最右侧来替换上段。

相关代码

class Solution:
    def maxRepOpt1(self, text: str) -> int:
        alphabet_map = {'':0}
        # 统计每个字符的最多数量
        for each in text:
            if each not in alphabet_map:
                alphabet_map[each] = 1
            else:
                alphabet_map[each] += 1
        # 上上段        
        last_last_alp = ''
        last_last_sum = 0
        # 上一段
        last_alp = ''
        last_sum = 0
        # 当前段
        now_alp = ''
        now_sum = 0
        ans = 0
        
        for each in text:
            if each == now_alp:
                now_sum += 1
            else:
                if last_sum == 1 and now_alp == last_last_alp:
                    if alphabet_map[now_alp] > last_last_sum + now_sum:
                        ans = max(ans, last_last_sum + now_sum + 1)
                    else:
                        ans = max(ans, last_last_sum + now_sum)
                else:
                    if alphabet_map[now_alp] > now_sum:
                        ans = max(ans, now_sum + 1)
                    else:
                        ans = max(ans, now_sum)
                        
                last_last_sum = last_sum
                last_last_alp = last_alp
                last_sum = now_sum
                last_alp = now_alp
                now_alp = each
                now_sum = 1
		# 结束后要把最后一段也统计一下
        if last_sum == 1 and now_alp == last_last_alp:
            if alphabet_map[now_alp] > last_last_sum + now_sum:
                ans = max(ans, last_last_sum + now_sum + 1)
            else:
                ans = max(ans, last_last_sum + now_sum)
        else:
            if alphabet_map[now_alp] > now_sum:
                ans = max(ans, now_sum + 1)
            else:
                ans = max(ans, now_sum)
                
        return ans

1157. 子数组中占绝大多数的元素

题目大意

实现一个 MajorityChecker 的类,它应该具有下述几个 API:

  • MajorityChecker(int[] arr) 会用给定的数组 arr 来构造一个 MajorityChecker 的实例。
  • int query(int left, int right, int threshold) 有这么几个参数:
    • 0 <= left <= right < arr.length 表示数组 arr 的子数组的长度。
    • 2 * threshold > right - left + 1,也就是说阀值 threshold 始终比子序列长度的一半还要大。

每次查询 query(…) 会返回在 arr[left], arr[left+1], …, arr[right] 中至少出现阀值次数 threshold 的元素,如果不存在这样的元素,就返回 -1。

解题思路

一般的想法query就把范围内的数都统计一下,这样就需要辅助的空间,而且效率上可能相对比较慢。
因为题目给定了条件,threshold 一定比查询范围长度的一半要长,那么答案要么是不存在,要么只有一个答案。
那么我们可以考虑把查询范围分成两段ab,若a段中没有超过一半的数,b段中也没有超过一半的数,那么肯定是不存在了;
所以要使其可能,则在a段中没有超过一半,在b段中必须超过一半才有可能(有点像数学中的反证法)。
那么我们从头开始遍历,开始以第一个数为基准,搜到第n个数时,是基准的数量和不是基准的数量正好一半一半,满足了a段中没有超过一半的数,那么我们直接看b段就行了,把基准赋值为b段中的第一个数,直至遍历完。
我们最后得到的基准即为有可能成为超过一半的查询范围长度的数,而非查询范围中最多的数,这点需要注意。

相关代码

c++代码

class MajorityChecker {
public:
    vector<int> my_arr;
    
    MajorityChecker(vector<int>& arr) {
        my_arr = arr;
    }
    
    int query(int left, int right, int threshold) {
    	//确定可能能大于一半的number
        int number = my_arr[left];
        int count = 1;
        for (int i = left + 1; i <= right; ++i)
        {
            if (my_arr[i] == number)
            {
                ++count;
            }   
            else if (count > 0)
            {
                --count;
            }
            else
            {
                count = 1;
                number = my_arr[i];
            }
        }
        // 计数
        count = 0;
        for (int i = left; i <= right; ++i)
            if (my_arr[i] == number)
                ++count;
        if (count >= threshold)
        {
            return number;   
        }
        
        return -1;     
    }
};

python3代码

class MajorityChecker:

    def __init__(self, arr: List[int]):
        self.arr = arr

    def query(self, left: int, right: int, threshold: int) -> int:
        temp = self.arr[left:right+1]
        dic = collections.Counter(temp)
        for key, val in dic.items():
            if val >= threshold:
                return key
        return -1

你可能感兴趣的:(leetcode周赛)