LeetCode之Weekly Contest 101

前一段时间比较忙,而且做这个对于我来说挺耗时间的,已经间隔了几期的没做总结了,后面有机会补齐。而且本来做这个的目的就是为了防止长时间不做把编程拉下,不在追求独立作出所有题了。以后完赛后稍微尝试下,做不出来的直接放弃。

第一题:问题

问题:900. RLE 迭代器

编写一个遍历游程编码序列的迭代器。

迭代器由 RLEIterator(int[] A) 初始化,其中 A 是某个序列的游程编码。更具体地,对于所有偶数iA[i] 告诉我们在序列中重复非负整数值 A[i + 1] 的次数。

迭代器支持一个函数:next(int n),它耗尽接下来的  n 个元素(n >= 1)并返回以这种方式耗去的最后一个元素。如果没有剩余的元素可供耗尽,则  next 返回 -1 。

例如,我们以 A = [3,8,0,9,2,5] 开始,这是序列 [8,8,8,5,5] 的游程编码。这是因为该序列可以读作 “三个零,零个九,两个五”。

示例:

输入:["RLEIterator","next","next","next","next"], [[[3,8,0,9,2,5]],[2],[1],[1],[2]]
输出:[null,8,8,5,-1]
解释:
RLEIterator 由 RLEIterator([3,8,0,9,2,5]) 初始化。
这映射到序列 [8,8,8,5,5]。
然后调用 RLEIterator.next 4次。

.next(2) 耗去序列的 2 个项,返回 8。现在剩下的序列是 [8, 5, 5]。

.next(1) 耗去序列的 1 个项,返回 8。现在剩下的序列是 [5, 5]。

.next(1) 耗去序列的 1 个项,返回 5。现在剩下的序列是 [5]。

.next(2) 耗去序列的 2 个项,返回 -1。 这是由于第一个被耗去的项是 5,
但第二个项并不存在。由于最后一个要耗去的项不存在,我们返回 -1。

提示:

  1. 0 <= A.length <= 1000
  2. A.length 是偶数。
  3. 0 <= A[i] <= 10^9
  4. 每个测试用例最多调用 1000 次 RLEIterator.next(int n)
  5. 每次调用 RLEIterator.next(int n) 都有 1 <= n <= 10^9 。

链接:https://leetcode-cn.com/contest/weekly-contest-101/problems/rle-iterator/

分析:

看到例子没怎么想,直接试图构建原始数据,然后next更新下标,根据下标直接返回,结果测试中数据很大,懒惰,想当然了。

最终做法是将原始数据建立坐标和值的映射关系,一个data数组,一个index数组(其实就相当于map),然后有变量记录下标,下标值大于index值后,更新到下一个,如果在其范围内,返回对应的数字。

比如例子给出的数据,处理后是:

3 8

5 5

表示坐标3一下的对应值是8,5一下的对应值是5。

AC Code:

class RLEIterator {
public:
        vector<long long> datas; //保存数据
    vector<long long> sizes; //保存坐标
    long long location; //数组坐标
    long long datalocation; //当前数字坐标
        long long size; //总个数
        int endflag; //一旦某个坐标返回-1,后面next都返回-1,做一个优化
public:
    RLEIterator(vector<int> A)
    {
        this->location = 0;
        this->datalocation = 0;
        this->size = 0;
                this->endflag = 0;

        for (int i = 0; i < A.size(); i+=2)
        {
            this->size += A[i];
            if (A[i] == 0)
            {
                continue;
            }
            if (i == 0)
            {
                this->sizes.emplace_back(A[i]);
            }
            else
            {
                this->sizes.emplace_back(A[i] + this->sizes[this->sizes.size()-1]);
            }

            this->datas.emplace_back(A[i+1]);
        }

    }

    int next(int n)
    {
                if (this->endflag == 1)
        {
            return -1;
        }
        if (this->datalocation + n>=this->size)
        {
            this->endflag = 1;
            return -1;
        }
        this->datalocation += n;

        while (this->datalocation > this->sizes[this->location])
        {
            this->location += 1;
        }
        return this->datas[this->location];
    }
};
     

其他:

1.第一code

class RLEIterator {
        int[] a;
        int p = 0;

        public RLEIterator(int[] A) {
            a = A;
            p = 0;
        }

        public int next(int n) {
            if(p >= a.length)return -1;
            n--;
            int ret = -1;
            while(n > 0){
                if(p >= a.length)return -1;
                int ex = Math.min(a[p], n);
                n -= ex;
                a[p] -= ex;
                if(a[p] > 0){
                    break;
                }else{
                    p += 2;
                }
            }
            while(p < a.length && a[p] == 0)p += 2;
            if(p < a.length)ret = a[p+1];
            n = 1;

            while(n > 0){
                if(p >= a.length)return -1;
                int ex = Math.min(a[p], n);
                n -= ex;
                a[p] -= ex;
                if(n == 0)break;
                p += 2;
            }
            return ret;
        }
    }    

2.中间有遇到一个问题,思路什么都多,提交错误,然后调试发现数据范围int是不够的。

第二题:901. 股票价格跨度

问题:

编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。

今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。

例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]

示例:

输入:["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]]
输出:[null,1,1,1,2,1,4,6]
解释:
首先,初始化 S = StockSpanner(),然后:
S.next(100) 被调用并返回 1,
S.next(80) 被调用并返回 1,
S.next(60) 被调用并返回 1,
S.next(70) 被调用并返回 2,
S.next(60) 被调用并返回 1,
S.next(75) 被调用并返回 4,
S.next(85) 被调用并返回 6。

注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格
(包括今天的价格 75) 小于或等于今天的价格。

提示:

  1. 调用 StockSpanner.next(int price) 时,将有 1 <= price <= 10^5
  2. 每个测试用例最多可以调用  10000 次 StockSpanner.next
  3. 在所有测试用例中,最多调用 150000 次 StockSpanner.next
  4. 此问题的总时间限制减少了 50%。

链接:https://leetcode-cn.com/contest/weekly-contest-101/problems/online-stock-span/

分析:

如果前一天的股票不大于今天的股票,就可以将其添加的今天的上面,然后将前一天的边界处再次进行判断。

如给出的例子中,

1 2 3 4 5 6 7
100 80 60 70 60 75 85

第一天100,最大连续天数1

第二天80,小于昨天,最大连续天数1

第三条60,小于昨天,最大连续天数1

第四天70,大于昨天,最大连续天数为1+昨天的连续天数1=2,同时查看前一天的80,大于70,第四天最大连续天数2

第五天60,小于昨天,最大连续天数1

第六天75,大于昨天,加上昨天的连续天数1+1,同时查看边界,即第四天,70<75,加上第四天的连续长度2,查看这一天的边界,即第二天,80>75,则最终最大连续天数为1+1+2=4

第七天85,大于昨天,最大连续天数1+4,查看前一天的边界,即第2天,80<85,加上改天的长度1,查看对比边界100>85,最终连续长度1+4+1=6

AC Code:

class StockSpanner {
public:
private:
    vector<long long> datas;
    vector<long long> longest;
public:
    StockSpanner()
    {

    }

    int next(int price)
    {
        if (this->datas.size() == 0)
        {
            //diyici
            this->datas.emplace_back(price);
            this->longest.emplace_back(1);
            return 1;
        }
        else
        {
            this->datas.emplace_back(price);
            if (this->datas[this->datas.size() - 2] > price)
            {
                this->longest.emplace_back(1);
                return 1;
            }
            else
            {

                int localindex = this->longest.size()-1;
                int ret = 1+this->longest[localindex];
                localindex -= this->longest[localindex];
                while (localindex>=0 && price>= this->datas[localindex])
                {
                    ret += this->longest[localindex];
                    localindex -= this->longest[localindex];
                }
                this->longest.emplace_back(ret);
                return ret;

            }

        }
    }
};

其他:

1.第一code

class StockSpanner {
        int[] stack;
        int[] value;
        int sp;
        int gen = 0;

        public StockSpanner() {
            stack = new int[11000];
            value = new int[11000];
            sp = 0;
            gen = 0;
        }

        public int next(int price) {
            while(sp > 0 && value[sp-1] <= price){
                sp--;
            }
            int ret = gen - (sp == 0 ? -1 : stack[sp-1]);
            stack[sp] = gen++;
            value[sp] = price;
            sp++;
            return ret;
        }
    }    

第三题:最大为 N 的数字组合

问题:

我们有一组排序的数字 D,它是  {‘1‘,‘2‘,‘3‘,‘4‘,‘5‘,‘6‘,‘7‘,‘8‘,‘9‘} 的非空子集。(请注意,‘0‘不包括在内。)

现在,我们用这些数字进行组合写数字,想用多少次就用多少次。例如 D = {‘1‘,‘3‘,‘5‘},我们可以写出像 ‘13‘, ‘551‘, ‘1351315‘ 这样的数字。

返回可以用 D 中的数字写出的小于或等于 N 的正整数的数目。

示例 1:

输入:D = ["1","3","5","7"], N = 100
输出:20
解释:
可写出的 20 个数字是:
1, 3, 5, 7, 11, 13, 15, 17, 31, 33, 35, 37, 51, 53, 55, 57, 71, 73, 75, 77.

示例 2:

输入:D = ["1","4","9"], N = 1000000000
输出:29523
解释:
我们可以写 3 个一位数字,9 个两位数字,27 个三位数字,
81 个四位数字,243 个五位数字,729 个六位数字,
2187 个七位数字,6561 个八位数字和 19683 个九位数字。
总共,可以使用D中的数字写出 29523 个整数。

提示:

  1. D 是按排序顺序的数字 ‘1‘-‘9‘ 的子集。
  2. 1 <= N <= 10^9

链接:https://leetcode-cn.com/contest/weekly-contest-101/problems/numbers-at-most-n-given-digit-set/

分析:

1.如果数字位数低于目标数字,肯定满足要求

2.如果位数相同,如果首位小于目标数字最高位,则剩下数字可以随意组合

通用的,如果最高位数字、次高位数字相同,剩下的位数可以随意组合,如此递归直到数字完全相同。

3.n个数字,组成m位数字,有n^m种组发。

AC Code:

class Solution {
public:

int atMostNGivenDigitSet(vector<string>& D, int N)
    {
        //先组成所有的数字,然后挑选满足条件的结果
        //首先拿到N的位数length,从D中挑选任意数字累计重复length次,得到所有可能的数字
        //大小为D.size^length,太多了,需要优化
        //关键是位数相同的一个,低于的位数可以直接计算指数,位数低一定满足
        //更关键的是最高位,

        long long ret = 0;
        vector<int> datas;
        for (long long i = 0; i < D.size(); i++)
        {
            datas.emplace_back(D[i][0] - ‘0‘);
        }
        if (GetLength(N) == 1)
        {
            return GetLessOrEqlN(datas, N).size();
        }
        long long num1 = GetSameLengthNums(datas, N);
        long long num2 = GetLowLengthNums(datas.size(), N / 10);
        ret = num1 + num2;
        return ret;
    }
    long long GetLowLengthNums(int size, int N)
    {
        long long length = GetLength(N);
        if (size == 1)  //1个数字组成任何位数数字都只有1种组发
        {
            return length;
        }
        long long ret = 0;
        while (length>0)
        {
            ret += (int)pow(size, length);
            length--;
        }
        return ret;
    }
    long long GetSameLengthNums(vector<int> datas, int N)
    {
        long long ret = 0;

        while (N)
        {
            if (N < 10)
            {
                ret += GetLessOrEqlN(datas, N).size();  //如果是10以下的数字,直接找到所有不大于该数的个数即可
                break;
            }
            int length = GetLength(N);
            int num = N / ((int)pow(10, length-1));
            ret += GetLessN(datas, num).size()*(int)pow(datas.size(), length-1);
            int findflag = 0;      //如果高位有相同的,还需要对比次高位,直到拼出相等数字
            for (int i = 0; i < datas.size(); i++)
            {
                int tmp = datas[i] - num;
                if (tmp==0)
                {
                    findflag = 1;
                    break;
                }
            }
            if (findflag == 0)
            {
                //cout << "no target,break" << endl;
                break;
            }
            N %= ((int)pow(10, length - 1));
                if (GetLength(N) + 1 != length)
            {
                break;
            }
        }

        return ret;
    }
    vector<int> GetLessN(vector<int> org, int n)
    {
        vector<int> ret;
        for (int i = 0; i < org.size(); i++)
        {
            if (org[i] < n)
            {
                ret.emplace_back(org[i]);
            }
        }
        return ret;
    }
    vector<int> GetLessOrEqlN(vector<int> org, int n)
    {
        vector<int> ret;
        for (int i = 0; i < org.size(); i++)
        {
            if (org[i] <= n)
            {
                ret.emplace_back(org[i]);
            }
        }
        return ret;
    }
    int GetLength(int N)
    {
        int ret = 0;
        while (N)
        {
            ret++;
            N /= 10;
        }
        return ret;
    }
};

其他:

1.第一code

    class Solution {
        public int atMostNGivenDigitSet(String[] D, int N) {
            int mask = 0;
            for(String s : D){
                mask ^= 1<<s.charAt(0)-‘0‘;
            }
            char[] s = Integer.toString(N+1).toCharArray();
            long dp = 0;
            int e = 1;
            for(int i = 0;i < s.length;i++){
                dp *= Integer.bitCount(mask);

                for(int j = 1;j < s[i]-‘0‘;j++){
                    if(mask<<~j<0){
                        dp+=e;
                    }
                }
                if(i > 0){
                    dp += Integer.bitCount(mask);
                }
                if(mask<<~(s[i]-‘0‘)>=0){
                    e = 0;
                }
            }
            return (int)dp;
        }
    }    

直接将N+1,避免了==的额外处理,这个思路非常好。

2.手残将复制写作==,折腾了半天才发现,单步调试直接优化跳过了,还以为数据类型问题导致不能直接对比。

3.做的时候大体code完成之后简单测试通过,提价错误,解决提交,又有错误,再次解决,感觉在不停的在打补丁,一方面说明思路不完整,没能直接覆盖到所有情况,另一方面进一步暴露出测试数据设计上的不足,之前就有发现这个问题,不过感觉没多少提高,实际上在周赛的时候一般只是测试下例子,基本没有构造数据测试,关键是没时间,其次能力不足,难以短时间构建出合理的数据。好在编写的测试函数能够将所有的测试用例都保存下,一旦修改code可以将之前测过的所有数据测一遍,避免解决一个问题导致前面的测例failed。

第四题:DI 序列的有效排列

问题:

我们给出 S,一个源于 {‘D‘, ‘I‘} 的长度为 n 的字符串 。(这些字母代表 “减少” 和 “增加”。)
有效排列 是对整数 {0, 1, ..., n} 的一个排列 P[0], P[1], ..., P[n],使得对所有的 i

  • 如果 S[i] == ‘D‘,那么 P[i] > P[i+1],以及;
  • 如果 S[i] == ‘I‘,那么 P[i] < P[i+1]

有多少个有效排列?因为答案可能很大,所以请返回你的答案模 10^9 + 7.

示例:

输入:"DID"
输出:5
解释:
(0, 1, 2, 3) 的五个有效排列是:
(1, 0, 3, 2)
(2, 0, 3, 1)
(2, 1, 3, 0)
(3, 0, 2, 1)
(3, 1, 2, 0)

提示:

  1. 1 <= S.length <= 200
  2. S 仅由集合 {‘D‘, ‘I‘} 中的字符组成。

链接:https://leetcode-cn.com/contest/weekly-contest-101/problems/valid-permutations-for-di-sequence/

分析:

这种大数据类型一直都很头疼,放弃不折腾了

AC Code:

其他:

第一code

class Solution {
        public int numPermsDISequence(String S) {
            int n = S.length();
            int[] a = new int[n+1];
            int p = 0;
            int len = 0;
            for(int i = 0;i < n;i++){
                if(S.charAt(i) == ‘D‘){
                    len++;
                }else{
                    a[p++] = len+1;
                    len = 0;
                }
            }
            a[p++] = len+1;

            int mod = 1000000007;
            long[][] C = new long[400 + 1][400 + 1];
            for (int i = 0; i <= 400; i++) {
                C[i][0] = 1;
                for (int j = 1; j <= i; j++) {
                    C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
                    if (C[i][j] >= mod)
                        C[i][j] -= mod;
                }
            }

            a = Arrays.copyOf(a, p);
            long[] dp = new long[p+1];
            dp[0] = 1;
            int all = 0;
            for(int i = 1;i <= p;i++){
                all += a[i-1];
                int s = 0;
                for(int j = i-1, sgn = 1;j >= 0;j--, sgn = -sgn){
                    s += a[j];
                    dp[i] += dp[j] * C[all][s] * sgn;
                    dp[i] %= mod;
                }
                if(dp[i] < 0)dp[i] += mod;
            }
            return (int)dp[p];
        }
    }
检举作弊 

总结:

TI8那一次没参加周赛,其他的几次都参加了,但是总结没做,有的是有完成四道题,有的还没做完,还有一个有写到草稿箱,第四个没完成所以没发布。工作了,有很多事情要做,很难专注算法的提高与练习,而且本身天赋有限,有些问题如果想搞懂需要花太多的时间与精力,虽说当初开始的目的就是增长见识,性格使然还是一直在尽量做出所有问题,努力追求完美,结果更不完美。以后要逐渐学会放弃,不管做到什么程度,尽量周二晚前完成总结。

学习不易,坚持更难,继续努力吧。

原文地址:https://www.cnblogs.com/youdias/p/9630720.html

时间: 2024-11-06 07:29:54

LeetCode之Weekly Contest 101的相关文章

LeetCode之Weekly Contest 93

第一题:二进制间距 问题: 给定一个正整数 N,找到并返回 N 的二进制表示中两个连续的 1 之间的最长距离. 如果没有两个连续的 1,返回 0 . 示例 1: 输入:22 输出:2 解释: 22 的二进制是 0b10110 . 在 22 的二进制表示中,有三个 1,组成两对连续的 1 . 第一对连续的 1 中,两个 1 之间的距离为 2 . 第二对连续的 1 中,两个 1 之间的距离为 1 . 答案取两个距离之中最大的,也就是 2 . 示例 2: 输入:5 输出:2 解释: 5 的二进制是 0

LeetCode(Weekly Contest 183)题解

0. 前言 中文版地址:https://leetcode-cn.com/contest/weekly-contest-183/ 英文版地址:https://leetcode.com/contest/weekly-contest-183/ 1. 题解 1.1 5376. 非递增顺序的最小子序列(1403. Minimum Subsequence in Non-Increasing Order) 中文版题目描述:https://leetcode-cn.com/problems/minimum-sub

【leetcode】Weekly Contest 91

leetcode周赛,早上起来发现没网,用热点意识模糊的a了三个水题. 1.Lemonade Change 简单模拟题,收到十元用五元钱找回,收到20元时优先用一张10一张5,如果10不够用3张5,如果没有就返回flase(贪心). 1 public boolean lemonadeChange(int[] bills) { 2 int five = 0; 3 int ten = 0; 4 int twenty = 0; 5 for (int i = 0; i < bills.length; i

【leetcode】Weekly Contest 94

题目不难,被第二题卡了半个多小时QAQ,另一个就是以后能用Hashmap和Hashset的绝不遍历. 1. Leaf-Similar Trees dfs.层次遍历把叶子节点遍历然后对比即可,只要是先遍历左节点后遍历右节点就行. 1 class Solution { 2 public boolean leafSimilar(TreeNode root1, TreeNode root2) { 3 ArrayList<Integer> res1 = new ArrayList<>();

Leetcode Weekly Contest 86

Weekly Contest 86 A:840. 矩阵中的幻方 3 x 3 的幻方是一个填充有从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等. 给定一个由整数组成的 N × N 矩阵,其中有多少个 3 × 3 的 "幻方" 子矩阵?(每个子矩阵都是连续的). 直接模拟即可,本来是签到题,由于粗心,浪费了时间. 1 class Solution { 2 public: 3 int numMagicSquaresInside(vector&l

128th LeetCode Weekly Contest Complement of Base 10 Integer

Every non-negative integer N has a binary representation.  For example, 5 can be represented as "101" in binary, 11 as "1011" in binary, and so on.  Note that except for N = 0, there are no leading zeroes in any binary representation.

Leetcode Weekly Contest 152

退役老人现在连leetcode都不会做了 = = 今天早上做了leetcode第三题题目看错了,加上比赛中间还在调投稿的实验,一心二用直接gg 总结下教训就是 本渣现在做题连题目都看不清就开始做.开始写题之前应当把样例过一遍,然后自己再造1-2个例子,然后再开始做 A题:统计素数的个数(素数筛或者sqrt(n)判断都可以),然后分别计算count! class Solution { public: int numPrimeArrangements(int n) { vector<int> ha

[Leetcode Weekly Contest]174

链接:LeetCode [Leetcode]5328. 方阵中战斗力最弱的 K 行 给你一个大小为?m?* n?的方阵?mat,方阵由若干军人和平民组成,分别用 0 和 1 表示. 请你返回方阵中战斗力最弱的?k?行的索引,按从最弱到最强排序. 如果第?i?行的军人数量少于第?j?行,或者两行军人数量相同但 i 小于 j,那么我们认为第 i 行的战斗力比第 j 行弱. 军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前. 输入:\(mat = [[1,1,0,0,0], [1,

108th LeetCode Weekly Contest Binary Subarrays With Sum

In an array A of 0s and 1s, how many non-empty subarrays have sum S? Example 1: Input: A = [1,0,1,0,1], S = 2 Output: 4 Explanation: The 4 subarrays are bolded below: [1,0,1,0,1] [1,0,1,0,1] [1,0,1,0,1] [1,0,1,0,1] Note: A.length <= 30000 0 <= S <