LeetCode 984. String Without AAA or BBB【贪心/字符串】中等

这篇博客介绍如何根据给定整数a和b,创建一个长度为a+b且仅包含a个'a'和b个'b'的字符串,同时确保不包含子串'aaa'和'bbb'。提供了多种贪心算法实现,包括按字母数量优势填充和分类讨论的策略。

Given two integers a and b, return any string s such that:

  • s has length a + b and contains exactly a 'a' letters, and exactly b 'b' letters,
  • The substring 'aaa' does not occur in s, and
  • The substring 'bbb' does not occur in s.

Example 1:

Input: a = 1, b = 2
Output: "abb"
Explanation: "abb", "bab" and "bba" are all correct answers.

Example 2:

Input: a = 4, b = 1
Output: "aabaa"

Constraints:

  • 0 <= a, b <= 100
  • It is guaranteed such an s exists for the given a and b.

题意:给定两个整数 A 和 B,返回任意字符串 S,要求满足:

  • S 的长度为 A + B,且正好包含 A 个 'a' 字母与 B 个 'b' 字母;
  • 子串 'aaa' 没有出现在 S 中;
  • 子串 'bbb' 没有出现在 S 中。

解法 贪心

首先想到的是贪心,哪个字母剩余的数量多,就优先使用哪个。不过这里需要分类讨论一下:

  • 两个字母的数量之差小于等于 2 时,比如 A = 4, B = 4; A = 4, B = 5; A = 4, B = 6 ,这些情况下,对于剩余数量更多的字母每次使用两个,对于另一种字母也每次使用两个,如果剩下一个就全部用完;
  • 两个字母的数量之差大于 2 时,比如 A = 4, B = 7, ans = bb a bb aa bb a ,此时对于剩余数量更多的字母,先每次使用两个,对于另一种字母则每次使用一个(使用两个的话,最后会剩下连续多个前一种字母,违背题意),直到两个字母的剩余数量变成第一种情况。

具体代码如下:

class Solution {
public:
    string strWithout3a3b(int a, int b) {
        string ans;
        char x = 'a', y = 'b';
        if (a < b) { swap(x, y); swap(a, b); } //使得a>=b,a:x,b:y
        bool diff = a >= b + 3; 
        while (a || b) { 
            ans += string(min(a, 2), x);
            a -= min(a, 2);
            if (b) {
                if (!diff) { 
                    ans += string(min(b, 2), y);
                    b -= min(b, 2);
                } else { //是否差3个及以上
                    ans += string(1, y);
                    b -= 1;
                }
            } 
            diff = a >= b + 3;
        }
        return ans;
    }
};

运行效率如下:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户
内存消耗:5.8 MB, 在所有 C++ 提交中击败了88.47% 的用户

或者这样写

class Solution {
public:
    string strWithout3a3b(int a, int b) {
        string ans;
        char x = 'a', y = 'b';
        if (a < b) { swap(x, y); swap(a, b); } //使得a>=b,a:x,b:y 
        while (a || b) { 
            if (a) { ans.push_back(x); --a; }
            if (a && a + 1 >= b) { ans.push_back(x); --a; }
            if (b) { ans.push_back(y); --b; }
            //此前a==b,此时a+1==b;
            //此前a==b+1,此时a==b
            //此前a==b+2,此时a==b+1
            //此前a>=b+3,此时a>=b+2 
            if (a && b && a <= b + 1) { ans.push_back(y); --b; }
        }
        return ans;
    }
};

运行效率如下:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户
内存消耗:5.9 MB, 在所有 C++ 提交中击败了85.41% 的用户

当然,具体的填充策略不止一种,还可以:

  • 两个字母的数量之差小于等于 2 时,比如 A = 4, B = 4; A = 4, B = 5; A = 4, B = 6 ,这些情况下,对于剩余数量更多的字母每次使用一个(另一种字母数量为零时全部使用),对于另一种字母也每次使用一个;
  • 两个字母的数量之差大于 2 时,比如 A = 4, B = 7, ans = bba ba ba ba bb ,此时对于剩余数量更多的字母,先每次使用两个,对于另一种字母则每次使用一个(使用两个的话,最后会剩下连续多个前一种字母,违背题意),直到两个字母的剩余数量变成第一种情况。

具体代码如下:

class Solution {
public:
    string strWithout3a3b(int a, int b) {
        string ans;
        char x = 'a', y = 'b';
        if (a < b) { swap(x, y); swap(a, b); } //使得a>=b,a:x,b:y
        bool diff = a >= b + 3; 
        while (a || b) { 
            if (!diff) {  
                if (b) {
                    ans += string(1, x);
                    --a;
                    ans += string(1, y);
                    --b; 
                } else {
                    ans += string(a, x);
                    a = 0;
                } 
            } else { //是否差3个及以上
                ans += string(2, x);
                a -= 2;
                ans += string(1, y);
                --b;
            }
            diff = a >= b + 3;
        }
        return ans;
    }
};

运行效率如下:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户
内存消耗:6 MB, 在所有 C++ 提交中击败了42.59% 的用户

或者这样写

class Solution {
public:
    string strWithout3a3b(int a, int b) {
        string ans;
        char x = 'a', y = 'b';
        if (a < b) { swap(x, y); swap(a, b); } //使得a>=b,a:x,b:y
        while (a || b) { 
            if (a) { ans.push_back(x); --a; };
            if (a >= b + 2) { ans.push_back(x); --a; }
            if (b) { ans.push_back(y); --b; }
        }
        return ans;
    }
};

运行效率如下:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户
内存消耗:5.8 MB, 在所有 C++ 提交中击败了89.41% 的用户

还可以这样写

  • 两个字母的数量之差小于 2 时,比如 A = 4, B = 4; A = 4, B = 5 ,这些情况下,对于剩余数量更多的字母每次使用一个(另一种字母数量为零时全部使用),对于另一种字母也每次使用一个;
  • 两个字母的数量之差大于等于 2 时,比如 A = 4, B = 7, ans = bba ba ba ba bb ,此时对于剩余数量更多的字母,先每次使用两个,对于另一种字母则每次使用一个,直到两个字母的剩余数量变成第一种情况。

对应的简化代码如下:

class Solution {
public:
    string strWithout3a3b(int a, int b) {
        string ans;
        char x = 'a', y = 'b';
        if (a < b) { swap(x, y); swap(a, b); } //使得a>=b,a:x,b:y
        while (a || b) { 
            if (a) { ans.push_back(x); --a; }; 
            if (a > b) { ans.push_back(x); --a; }           
            if (b) { ans.push_back(y); --b; } 
        }
        return ans;
    }
}; 

运行效率如下:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户
内存消耗:6.1 MB, 在所有 C++ 提交中击败了20.23% 的用户

本题的递归写法如下

class Solution {
public:
    string strWithout3a3b(int a, int b) {
        if (a == 0) return string(b, 'b');
        if (b == 0) return string(a, 'a');
        if (a == b) return "ab" + strWithout3a3b(a - 1, b - 1);
        return a > b ? "aab" + strWithout3a3b(a - 2, b - 1) : "bba" + strWithout3a3b(a - 1, b - 2);
    }
}; 

运行效率如下:

执行用时:0 ms, 在所有 C++ 提交中击败了100.00% 的用户
内存消耗:6.1 MB, 在所有 C++ 提交中击败了16.94% 的用户
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

memcpy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值