【数据结构-邻项消除】力扣1717. 删除子字符串的最大得分

给你一个字符串 s 和两个整数 x 和 y 。你可以执行下面两种操作任意次。

删除子字符串 "ab" 并得到 x 分。

比方说,从 "cabxbae" 删除 ab ,得到 "cxbae" 。

删除子字符串"ba" 并得到 y 分。

比方说,从 "cabxbae" 删除 ba ,得到 "cabxe" 。

请返回对 s 字符串执行上面操作若干次能得到的最大得分。

示例 1:

输入:s = "cdbcbbaaabab", x = 4, y = 5

输出:19

解释:

  • 删除 "cdbcbbaaabab" 中加粗的 "ba" ,得到 s = "cdbcbbaaab" ,加 5 分。
  • 删除 "cdbcbbaaab" 中加粗的 "ab" ,得到 s = "cdbcbbaa" ,加 4 分。
  • 删除 "cdbcbbaa" 中加粗的 "ba" ,得到 s = "cdbcba" ,加 5 分。
  • 删除 "cdbcba" 中加粗的 "ba" ,得到 s = "cdbc" ,加 5 分。
    总得分为 5 + 4 + 5 + 5 = 19 。

示例 2:

输入:s = "aabbaaxybbaabb", x = 5, y = 4

输出:20

模拟栈

cpp 复制代码
class Solution {
public:
    int maximumGain(string s, int x, int y) {
        int a1, a2;
        string b1, b2;
        if(x > y){
            a1 = x;
            a2 = y;
            b1 = "ab";
            b2 = "ba";
        } else {
            a1 = y;
            a2 = x;
            b1 = "ba";
            b2 = "ab";
        }

        int ans = 0;
        string st1 = "";
        // 优先删除高分组合
        for(char c : s){
            st1.push_back(c);
            if(st1.size() >= 2 && st1.substr(st1.size() - 2, 2) == b1){
                st1.erase(st1.end() - 2, st1.end());
                ans += a1;
            }
        }

        string st2 = "";
        // 删除低分组合
        for(char c : st1){
            st2.push_back(c);
            if(st2.size() >= 2 && st2.substr(st2.size() - 2, 2) == b2){
                st2.erase(st2.end() - 2, st2.end());
                ans += a2;
            }
        }
        return ans;
    }
};

这是我一开始采用的方式,核心思路是我们要优先删除分数更大的字符串,然后再去删除分数较小的字符串。我们首先定义一个栈st1,不断往里面推入字符串s的字符,每当推入字符后,栈从上往下第一个元素和第二个元素如果连起来等于分数较大的字符串,那么我们就将它删除,并加上最大得分。然后再定义一个栈st2,遍历处理过一遍的字符串st1,遍历里面的字符,逐一删除掉较小的分的字符串,并加上相应的得分。最后ans就是最大得分。

贪心

cpp 复制代码
class Solution {
public:
    int maximumGain(string s, int x, int y) {
        int n = s.size();
        if(x < y){
            swap(x, y);
            for(int i = 0; i < n; i++){
                if(s[i] == 'a') s[i] = 'b';
                else if(s[i] == 'b') s[i] = 'a';
            }
        }

        int ret = 0;
        int i = 0;
        while(i < n){
            while(i < n && s[i] != 'a' && s[i] != 'b') i++;

            int ca = 0, cb = 0;
            while(i < n && (s[i] == 'a' || s[i] == 'b')){
                if(s[i] == 'a'){
                    ca++;
                }
                else{
                    if(ca > 0){
                        ca--;
                        ret += x;  
                    }else{
                        cb++;
                    }
                }
                i++;
            }
            ret += min(ca, cb) * y;
        }
        return ret;
    }
};

该方法的时间复杂度和空间复杂度都会更优。

首先核心思路不变,先删除得分较大的字符串。在这里只有两个字符串"ab"和"ba",不妨假设 "ab" 的得分总是不低于 "ba";否则,我们将字符串中的字符 a 换成 b,b 换成 a,再交换 x 和 y 即可。

接下来是主体部分:

第一个while的目的是让指针遍历完数组。

第二个while的目的是跳过除了a或者b以外的其他字符。

第三个while的目的是记录在一个只有a和b字符的部分,a的数量和b的数量是多少。该while是贪心的思想的核心,我们因为已经假设ab的得分较大,那么当遍历字符是a的时候,我们直接记录a的数量,当字符是b的时候,并且在该部分字符串中,依旧存在a,那么他们就最终能构成字符串"ab"。如果不存在a,那么就记录b的数量。我们记录完删除"ab"得到的得分后,还要记录删除"ba"得到的得分,这取决于在删除完"ab"操作后,剩余的a和b的数量的较小值乘以y。

最后ret就是最大得分。

相关推荐
XH华13 分钟前
初识C语言之二维数组(下)
c语言·算法
南宫生34 分钟前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_1 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子1 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡1 小时前
滑动窗口 + 算法复习
数据结构·算法
Lenyiin1 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码1 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
scan7242 小时前
LILAC采样算法
人工智能·算法·机器学习
菌菌的快乐生活2 小时前
理解支持向量机
算法·机器学习·支持向量机
大山同学2 小时前
第三章线性判别函数(二)
线性代数·算法·机器学习