LeetCode冲刺:Day11

发布于 2022-11-19  108 次阅读


12点刚过,该睡觉了。

1. 字符串的前缀分数和

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

与前缀树相关,这个东西比较有意思,放后面研究一下。

代码

2. 统计共同度过的日子数

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

写三个(准确来说是俩)辅助函数。一个用于计算起止日期间有多少天;一个用于返回两个日期中较大的那个;一个用于返回两个日期中较小的那个。

然后取到达时间中最大的那个,离开时间中最小的那个,作为 起止日期,计算期间天数即可。

代码

class Solution {
private:
    const vector<int> dayOfMonth={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    // 定义a到b有多少天
    int totalDay(string a,string b) {
        int ans=0;
        int sum=0;
        int startMonth=(a[0]-'0')*10+(a[1]-'0');
        int endMonth=(b[0]-'0')*10+(b[1]-'0');
        int startDay=(a[3]-'0')*10+(a[4]-'0');
        int endDay=(b[3]-'0')*10+(b[4]-'0');
        int startMonthSurplus=startDay-1;
        int endMonthSurplus=dayOfMonth[endMonth-1]-endDay;
        for (int i=startMonth;i<=endMonth;++i) sum+=dayOfMonth[i-1];
        ans = sum-endMonthSurplus-startMonthSurplus;
        return ans<0?0:ans;
    }
    string maxDay(string a,string b) {
        if (a[0]-'0'>b[0]-'0') return a;
        if (a[0]-'0'==b[0]-'0'&&a[1]-'0'>b[1]-'0') return a;
        if (a[0]-'0'==b[0]-'0'&&a[1]-'0'==b[1]-'0'&&a[3]-'0'>b[3]-'0') return a;
        if (a[0]-'0'==b[0]-'0'&&a[1]-'0'==b[1]-'0'&&a[3]-'0'==b[3]-'0'&&a[4]-'0'>b[4]-'0') return a;
        return b;
    }

    string minDay(string a,string b) {
        if (maxDay(a,b)==a) return b;
        else return a;
    }
public:
    int countDaysTogether(string arriveAlice, string leaveAlice, string arriveBob, string leaveBob) {
        string start=max(arriveAlice,arriveBob);
        string end=min(leaveAlice,leaveBob);
        return totalDay(start,end);
    }
};

3. 运动员和训练师的最大匹配数

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

排序后逐个匹配即可。这题明显比上一个Easy简单……

代码

class Solution {
public:
    int matchPlayersAndTrainers(vector<int>& players, vector<int>& trainers) {
        int i=0,j=0,p=players.size(),t=trainers.size(),ans=0;
        sort(players.begin(),players.end());
        sort(trainers.begin(),trainers.end());
        while(i<p&&j<t) {
            if (players[i]<=trainers[j]) {
                ++i;++j;++ans;
            } else {
                ++j;
            }
        }
        return ans;
    }
};

4. 按位或最大的最小子数组长度

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

参考这篇

动态规划,时间复杂度O(n)。或运算是只要有一个1,就变为1。从位的角度考虑,右侧每一个数字都对最大值有某几位1的贡献,我们需要找到最近的那个最远1由谁贡献,则距离可以确定。

代码

class Solution {
public:
    vector<int> smallestSubarrays(vector<int>& nums) {
        int n=nums.size();
        vector<int> ans(n);
        vector<int> dp(32,-1);
        for (int i=n-1;i>=0;--i) {
            int maxLen=1;
            for (int j=0;j<32;++j) {
                if ((nums[i]>>j)&1==1) dp[j]=i;
                if (dp[j]!=-1) maxLen=max(maxLen,dp[j]-i+1);
            }
            ans[i]=maxLen;
        }
        return ans;
    }
};

5. 完成所有交易的初始最少钱数

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

参考这篇 ,优雅,实在是太优雅了。

考虑最坏情况,所有亏钱交易先进行,得到一个总的亏钱量。然后考虑两种情况,一个是亏完钱后马上进行一个不亏钱交易,但这个不亏钱交易的cost要最大;一个是最后一个亏钱交易,它的cost最大。就看这两种情况下,哪个最大。

对于最坏情况的分析值得学习。

代码

class Solution {
public:
    long long minimumMoney(vector<vector<int>>& transactions) {
        long long mx=0;
        long long totalLost=0;
        int n=transactions.size();
        for (int i=0;i<n;++i) {
            totalLost+=max(transactions[i][0]-transactions[i][1],0);
            mx=max(mx,(long long)min(transactions[i][0],transactions[i][1]));
        }
        return totalLost+mx;
    }
};

6. 出现最频繁的偶数元素

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

简单模拟。

用按位与判断奇偶,不知道为什么进不了if……

代码

class Solution {
public:
    int mostFrequentEven(vector<int>& nums) {
        map<int,int> mp;
        for (int i=0;i<nums.size();++i) if (nums[i]%2==0) mp[nums[i]]++;
        int maxCnt=INT_MIN;
        int ans=-1;
        for (auto& elem:mp) {
            if (elem.second==maxCnt) ans=elem.first<ans?elem.first:ans;
            if (elem.second>maxCnt) {
                maxCnt=elem.second;
                ans=elem.first;
            }
        }
        return ans;
    }
};

7. 子字符串的最优划分

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

意外简单,一次遍历即可。

维护一个26元素的数组last,记录当前字母上次出现的下标,如果为-1,则赋当前下标,如果不为-1,说明必须要划分一次,++ans后重置last数组,并且--下标。

代码

class Solution {
public:
    int partitionString(string s) {
        int n=s.size();
        vector<int> last(26,-1);
        vector<int> temp(26,-1);
        int ans=0;
        for (int i=0;i<n;++i) {
            if (last[s[i]-'a']==-1) last[s[i]-'a']=i;
            else {
                ans++;
                last=temp;
                --i;
            }
        }
        return ans+1;
    }
};

8. 将区间分为最少组数

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

模拟的做法虽然优化了,依然超时。

答案用了小根堆来存储每个组的最大结束时间。这块内容还需要复习。

代码

class Solution {
public:
    static bool cmp(vector<int>& a,vector<int>& b) {
        if (a[0]<b[0]) return true;
        else if (a[0]==b[0]) return a[1]<b[1];
        else return false;
    }
    int minGroups(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end(),cmp);
        int n=intervals.size();
        priority_queue<int,vector<int>,greater<int>> pq;
        for (auto& elem:intervals) {
            if (!pq.empty()&&elem[0]>pq.top()) pq.pop();
            pq.push(elem[1]);
        }       
        return pq.size();
    }
};

9. 检查相同字母间的距离

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

用一个last数组记录字母上次出现位置即可。

代码

class Solution {
public:
    bool checkDistances(string s, vector<int>& distance) {
        vector<int> last(26,-1);
        int n=s.size();
        for (int i=0;i<n;++i) {
            if (last[s[i]-'a']==-1) last[s[i]-'a']=i;
            else {
                if (i-last[s[i]-'a']-1!=distance[s[i]-'a']) return false;
            }
        }
        return true;
    }
};

10. 恰好移动 k 步到达某一位置的方法数目

难度

  • Easy
  • Medium
  • Hard

完成情况

  • 独立完成
  • 参考思路
  • 参考答案

思路

我的想法是转换成求大数阶乘问题。

要从start走到end,无论以什么顺序走,必有abs(end-start)步是确定要走的。那么剩下m=k-abs(end-start)步。如果m为奇,返回0。那么剩下的事情,就是(m/2+abs(end-start))步往一个方向走,(m/2)步往另一个方向走,共有多少种走法,是一个排列组合问题。求组合数可以利用公式,n的平方复杂度。

求组合数参考这篇

代码

class Solution {
    const int MOD = 1000000007;

public:
    int numberOfWays(int startPos, int endPos, int K) {
        int d = abs(startPos - endPos);
        if ((d + K) % 2 == 1 || d > K) return 0;
        // 递推求组合数
        vector<vector<long long>> f;
        f.resize(K + 1, vector<long long>(K + 1));
        for (int i = 0; i <= K; i++) {
            f[i][0] = 1;
            for (int j = 1; j <= i; j++) f[i][j] = (f[i - 1][j] + f[i - 1][j - 1]) % MOD;
        }
        return f[K][(d + K) / 2];
    }
};