Leetcode周赛374总结

Q1 找出峰值

  • 题目链接

  • 解题思路:

    • 一次遍历,依次判断元素是否同时大于其左侧和右侧元素,如果是,则将其下标加到答案数组中
  • 解题代码:


class Solution {
public:
    vector<int> findPeaks(vector<int>& mountain) {
        vector<int> ans;
        int n = mountain.size();
        for(int i = 1; i < n-1; i++)
        {
            if(mountain[i] > mountain[i-1] && mountain[i] > mountain[i+1])
                ans.push_back(i);
        }
        return ans;
    }
};

Q2 需要添加的硬币的最小数量

  • 题目链接

  • 解题思路:

    • 假设当前可以表示[0, s-1]范围中的数,新来了一个数x,可以新表示的范围是[x, s+x-1]

      • 如果x <= s,两个区间可以合并成[0, s+x-1]
      • 如果x > s,会存在[s, x-1]范围内的数无法被表示,需要添加s到数组中
    • 时间复杂度:O(nlogn + log target)

  • 解题代码:


class Solution {
public:
    int minimumAddedCoins(vector<int>& coins, int target) {
        int n = coins.size();
        int idx = 0;
        int s = 1;
        int ans = 0;
        sort(coins.begin(), coins.end());
        while(s <= target)
        {
            if(idx < n && coins[idx] <= s)
            {
                s += coins[idx];
                idx += 1;
            }
            else
            {
                s += s;
                ans += 1;
            }
        }
        return ans;
    }
};

Q3 统计完全字符串

  • 题目链接
  • 解题思路:
    • 相邻字符在字母表中的顺序至多相差2,根据这一条件将原字符串分割成一块一块的
    • 对每一块进行处理:
      • 枚举子串中出现的字符个数m,这样子串的长度就是mk
      • 在每一块子串上检查长度为mk的子串,是否满足其中每个字符的出现次数都是k,如果是则答案+1(滑动窗口)
    • 时间复杂度:O(26 * 26 * n)
  • 解题代码:

class Solution {
public:
    int countCompleteSubstrings(string word, int k) {
        auto check = [&](vector<int>& cnt) -> bool
        {
            for(int i = 0; i < 26; i++)
            {
                if(cnt[i] != 0 && cnt[i] != k)
                    return false;
            }
            return true;
        };
       
        auto func = [&](int start, int end) -> int
        {
            int res = 0;
            for(int i = 1; i <= 26; i++)
            {
                int size = i * k;
                if(size > end - start + 1)
                    break;
                vector<int> cnt(26, 0);
                int left = start, right = start;
                while(right < left + size)
                {    
                    cnt[word[right] - 'a'] += 1;
                    right += 1;
                }
                if(check(cnt))
                    res += 1;
                while(right <= end)
                {
                    cnt[word[right] - 'a'] += 1;
                    right += 1;
                    cnt[word[left] - 'a'] -= 1;
                    left += 1;
                    if(check(cnt))
                        res += 1;
                }
            }
            return res;
        };
        int n = word.size();
        int ans = 0;
        for(int i = 0; i < n; i++)
        {
            int j = i + 1;
            while(j < n && abs(int(word[j]) - int(word[j-1])) <= 2)
                j += 1;
            ans += func(i, j-1);
            i = j-1;
        }
        return ans;
    }
};

  • 优化时间复杂度,对出现次数再进行一次哈希,判断出现k次的字符是否有m个,时间复杂度可以优化到O(26n)
  • 解题代码:
class Solution {
public:
    int countCompleteSubstrings(string word, int k) {        
        auto func = [&](int start, int end) -> int
        {
            int res = 0;
            for(int i = 1; i <= 26; i++)
            {
                int size = i * k;
                if(size > end - start + 1)
                    break;
                vector<int> cnt(26, 0);
                unordered_map<int, int> um;
                int left = start, right = start;
                while(right < left + size)
                {    
                    um[cnt[word[right] - 'a']] -= 1;
                    cnt[word[right] - 'a'] += 1;
                    um[cnt[word[right] - 'a']] += 1;
                    right += 1;
                }
                if(um[k] == i)
                    res += 1;
                while(right <= end)
                {
                    um[cnt[word[right] - 'a']] -= 1;
                    cnt[word[right] - 'a'] += 1;
                    um[cnt[word[right] - 'a']] += 1;
                    right += 1;
                    um[cnt[word[left] - 'a']] -= 1;
                    cnt[word[left] - 'a'] -= 1;
                    um[cnt[word[left] - 'a']] += 1;
                    left += 1;
                    if(um[k] == i)
                        res += 1;
                }
            }
            return res;
        };
        int n = word.size();
        int ans = 0;
        for(int i = 0; i < n; i++)
        {
            int j = i + 1;
            while(j < n && abs(int(word[j]) - int(word[j-1])) <= 2)
                j += 1;
            ans += func(i, j-1);
            i = j-1;
        }
        return ans;
    }
};

Q4 统计感冒序列的数目

  • 题目链接

  • 解题思路:

    • 对于每一段[l, r],感染方案数是 2^(m-1),其中m = r - l -1;
    • 不同段之间是完全独立的
      • 设有m1, m2, m3三段,其和为s
      • 那么方案数为:C(s, m1) * C(s-m1, m2) * C(s-m1-m2, m3) * 2^(m1+m2+m3-3)
  • 解题代码:


const int MX = 100000;
const int MOD = 1e9+7;
long long fac[MX], inv_fac[MX];
long long myPow(long long a, int n)
{
    if(n == 0)
        return 1;
    if(n & 1)
        return (a * myPow(a, n-1)) % MOD;
    long long temp = myPow(a, n/2);
    return temp * temp % MOD;
}
auto init = [] {
    fac[0] = 1;
    for(int i = 1; i < MX; i++)
        fac[i] = fac[i-1] * i % MOD;
    inv_fac[MX-1] = myPow(fac[MX-1], MOD-2);
    for(int i = MX-1; i > 0; i--)
        inv_fac[i-1] = inv_fac[i] * i % MOD;
    return 0;
}();
long long comb(int n, int k)
{
    return fac[n] * inv_fac[k] % MOD * inv_fac[n-k] % MOD;
}
class Solution {
public:
    int numberOfSequence(int n, vector<int>& sick) {
        int m = sick.size();
        int total = n - m;
        long long ans = comb(total, sick[0]) * comb(total - sick[0], n - sick.back() - 1) % MOD;
        total -= sick[0] + n - sick.back() - 1;
        int e = 0;
        for(int i = 0; i < m-1; i++)
        {
            int k = sick[i+1] - sick[i] - 1;
            if(k != 0)
            {
                e += k-1;
                ans = (ans * comb(total, k)) % MOD;
                total -= k;
            }
        }
        return ans * myPow(2, e) % MOD;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值