187. 重复的DNA序列;227. 基本计算器 II;767. 重构字符串

所有 DNA 都由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。

编写一个函数来查找目标子串,目标子串的长度为 10,且在 DNA 字符串 s 中出现次数超过一次。

 

示例:

输入:s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"
输出:["AAAAACCCCC", "CCCCCAAAAA"]

class Solution {
public:
    vector<string> findRepeatedDnaSequences(string s) {
        if (s.size() <= 10) return {};
        unordered_map<char, int> char_to_val = {
            {'A', 0}, {'C', 1}, {'G', 2}, {'T', 3}  // 1 2 3 4 不正确
        };
        int mod = pow(4, 10 - 1);
        int val = 0;  
        unordered_map<int, int> reval; 
        vector<string> res;    
        for (int i = 0; i < s.size(); ++i) {
            if (i >= 10) {
                val = ((val % mod) << 2) + char_to_val[s[i]];
                if (++reval[val] == 2) {            // 只保存第二次重复出现的字符串,二次以上不需要计数
                    res.push_back(s.substr(i - 9, 10));
                }
            } else {
                val = (val << 2) + char_to_val[s[i]];
                if (i == 9) {
                    ++reval[val];
                }
            }
        }
        return res;
    }
};

实现一个基本的计算器来计算一个简单的字符串表达式的值。

字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格  。 整数除法仅保留整数部分。

示例 1:

输入: "3+2*2"
输出: 7


示例 2:

输入: " 3/2 "
输出: 1

示例 3:

输入: " 3+5 / 2 "
输出: 5


说明:


    你可以假设所给定的表达式都是有效的。
    请不要使用内置的库函数 eval。

class Solution {    // 测试用例处理不了 "1+1 2"的情况,所以不考虑,但得考虑连续空格的情况"  1  +  2  "
public:
    int calculate(string s) {
        deque<int> sta_int;
        deque<char> sta_char;
        for (int i = 0, n = 0; i <= s.size(); ++i) {
            if (i == s.size() || s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/') {
                if (n == 0) continue;
                sta_int.push_back(stoi(s.substr(i - n, n)));
                while (sta_char.size() && (sta_char.back() == '*' || sta_char.back() == '/')) { // 计算 * /
                    auto b = sta_int.back();
                    sta_int.pop_back();
                    auto a = sta_int.back();
                    sta_int.pop_back();
                    sta_int.push_back(helper(a, b, sta_char.back()));   // a b出双端队列头的顺序
                    sta_char.pop_back();
                }
                if (i == s.size()) {
                    break;
                }
                sta_char.push_back(s[i]);
                n = 0;
            } else {
                ++n;
            }
        }
        while (sta_char.size()) {   // 计算 + -
            auto a = sta_int.front();
            sta_int.pop_front();
            auto b = sta_int.front();
            sta_int.pop_front();
            sta_int.push_front(helper(a, b, sta_char.front()));         // a b出双端队列头的顺序
            sta_char.pop_front();
        }
        return sta_int.back();
    }
    int helper(const int& a, const int& b, const char& c) {
        switch (c) {
            case '+': return a + b;
            case '-': return a - b;
            case '*': return a * b;
            case '/': return a / b;
        }
        return INT_MAX;
    }
};

给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。

若可行,输出任意可行的结果。若不可行,返回空字符串。

示例 1:

输入: S = "aab"
输出: "aba"


示例 2:

输入: S = "aaab"
输出: ""


注意:


    S 只包含小写字母并且长度在[1, 500]区间内。

前言
这道题是典型的贪心算法的题。重构字符串时,需要根据每个字母在字符串中出现的次数处理每个字母放置的位置。如果出现次数最多的字母可以在重新排布之后不相邻,则可以重新排布字母使得相邻的字母都不相同。如果出现次数最多的字母过多,则无法重新排布字母使得相邻的字母都不相同。
假设字符串的长度为 nnn,如果可以重新排布成相邻的字母都不相同的字符串,每个字母最多出现多少次?
当 nnn 是偶数时,有 n/2n/2n/2 个偶数下标和 n/2n/2n/2 个奇数下标,因此每个字母的出现次数都不能超过 n/2n/2n/2 次,否则出现次数最多的字母一定会出现相邻。
当 nnn 是奇数时,由于共有 (n+1)/2(n+1)/2(n+1)/2 个偶数下标,因此每个字母的出现次数都不能超过 (n+1)/2(n+1)/2(n+1)/2 次,否则出现次数最多的字母一定会出现相邻。
由于当 nnn 是偶数时,在整数除法下满足 n/2n/2n/2 和 (n+1)/2(n+1)/2(n+1)/2 相等,因此可以合并 nnn 是偶数与 nnn 是奇数的情况:如果可以重新排布成相邻的字母都不相同的字符串,每个字母最多出现 (n+1)/2(n+1)/2(n+1)/2 次。
因此首先遍历字符串并统计每个字母的出现次数,如果存在一个字母的出现次数大于 (n+1)/2(n+1)/2(n+1)/2,则无法重新排布字母使得相邻的字母都不相同,返回空字符串。如果所有字母的出现次数都不超过 (n+1)/2(n+1)/2(n+1)/2,则考虑如何重新排布字母。
以下提供两种使用贪心算法的方法,分别基于最大堆和计数。
 方法一:基于最大堆的贪心算法
维护最大堆存储字母,堆顶元素为出现次数最多的字母。首先统计每个字母的出现次数,然后将出现次数大于 000 的字母加入最大堆。
当最大堆的元素个数大于 111 时,每次从最大堆取出两个字母,拼接到重构的字符串,然后将两个字母的出现次数分别减 111,并将剩余出现次数大于 000 的字母重新加入最大堆。由于最大堆中的元素都是不同的,因此取出的两个字母一定也是不同的,将两个不同的字母拼接到重构的字符串,可以确保相邻的字母都不相同。
如果最大堆变成空,则已经完成字符串的重构。如果最大堆剩下 111 个元素,则取出最后一个字母,拼接到重构的字符串。
对于长度为 nnn 的字符串,共有 n/2n/2n/2 次每次从最大堆取出两个字母的操作,当 nnn 是奇数时,还有一次从最大堆取出一个字母的操作,因此重构的字符串的长度一定是 nnn。
当 nnn 是奇数时,是否可能出现重构的字符串的最后两个字母相同的情况?如果最后一个字母在整个字符串中至少出现了 222 次,则在最后一次从最大堆取出两个字母时,该字母会先被选出,因此不会成为重构的字符串的倒数第二个字母,也不可能出现重构的字符串最后两个字母相同的情况。
因此,在重构字符串可行的情况下,基于最大堆的贪心算法可以确保得到正确答案。

class Solution {
public:
    string reorganizeString(string S) {
        vector<int> vec(26, 0);
        int max_cnt = 0;
        string res = "";
        for (auto &s: S) {
            ++vec[s - 'a'];
            max_cnt = max(max_cnt, vec[s - 'a']);
        }
        if (max_cnt > (S.size() + 1)/2) 
            return "";
        auto cmp = [&vec](const int& a, const int& b) {                //
            return vec[a] < vec[b];
        };
        priority_queue<int, vector<int>, decltype(cmp)> p_que{cmp};    //
        for (int i = 0; i < 26; ++i)
            if (vec[i])
                p_que.push(i);
        while (p_que.size() > 1) {
            auto a = p_que.top();
            p_que.pop();
            auto b = p_que.top();
            p_que.pop();
            res = res + static_cast<char>(a + 'a') + static_cast<char>(b + 'a');    // res += 出错
            if (--vec[a]) p_que.push(a);
            if (--vec[b]) p_que.push(b);
        }
        if (p_que.size())
            res += static_cast<char>(p_que.top() + 'a');
        return res;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值