iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++实现LeetCode(241.添加括号的不同方式)
  • 153
分享到

C++实现LeetCode(241.添加括号的不同方式)

2024-04-02 19:04:59 153人浏览 薄情痞子
摘要

[LeetCode] 241. Different Ways to Add Parentheses 添加括号的不同方式 Given a string of numbers and o

[LeetCode] 241. Different Ways to Add Parentheses 添加括号的不同方式

Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +, - and *.

Example 1:

Input:

"2-1-1"

Output:

[0, 2]

Explanation:
((2-1)-1) = 0
(2-(1-1)) = 2

Example 2:

Input: "2*3-4*5"
Output: [-34, -14, -10, -10, 10]
Explanation:
(2*(3-(4*5))) = -34
((2*3)-(4*5)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10

这道题让给了一个可能含有加减乘的表达式,让我们在任意位置添加括号,求出所有可能表达式的不同值。这道题乍一看感觉还蛮难的,给人的感觉是既要在不同的位置上加括号,又要计算表达式的值,结果一看还是道 Medium 的题,直接尼克杨问号脸?!遇到了难题不要害怕,从最简单的例子开始分析,慢慢的找规律,十有八九就会在分析的过程中灵光一现,找到了破题的方法。这道题貌似默认输入都必须是合法的,虽然题目中没有明确的指出这一点,所以我们也就不必进行 valid 验证了。先从最简单的输入开始,若 input 是空串,那就返回一个空数组。若 input 是一个数字的话,那么括号加与不加其实都没啥区别,因为不存在计算,但是需要将字符串转为整型数,因为返回的是一个整型数组。当然,input 是一个单独的运算符这种情况是不存在的,因为前面说了这道题默认输入的合法的。下面来看若 input 是数字和运算符的时候,比如 "1+1" 这种情况,那么加不加括号也没有任何影响,因为只有一个计算,结果一定是2。再复杂一点的话,比如题目中的例子1,input 是 "2-1-1" 时,就有两种情况了,(2-1)-1 和 2-(1-1),由于括号的不同,得到的结果也不同,但如果我们把括号里的东西当作一个黑箱的话,那么其就变为 ()-1  和 2-(),其最终的结果跟括号内可能得到的值是息息相关的,那么再 general 一点,实际上就可以变成 () ? () 这种形式,两个括号内分别是各自的表达式,最终会分别计算得到两个整型数组,中间的问号表示运算符,可以是加,减,或乘。那么问题就变成了从两个数组中任意选两个数字进行运算,瞬间变成我们会做的题目了有木有?而这种左右两个括号代表的黑盒子就交给递归去计算,像这种分成左右两坨的 pattern 就是大名鼎鼎的分治法 Divide and Conquer 了,是必须要掌握的一个神器。类似的题目还有之前的那道 Unique Binary Search Trees II 用的方法一样,用递归来解,划分左右子树,递归构造。

好,继续来说这道题,我们不用新建递归函数,就用其本身来递归就行,先建立一个结果 res 数组,然后遍历 input 中的字符,根据上面的分析,我们希望在每个运算符的地方,将 input 分成左右两部分,从而扔到递归中去计算,从而可以得到两个整型数组 left 和 right,分别表示作用两部分各自添加不同的括号所能得到的所有不同的值,此时我们只要分别从两个数组中取数字进行当前的运算符计算,然后把结果存到 res 中即可。当然,若最终结果 res 中还是空的,那么只有一种情况,input 本身就是一个数字,直接转为整型存入结果 res 中即可,参见代码如下:

解法一:


class Solution {
public:
    vector<int> diffWaysToCompute(string input) {
        vector<int> res;
        for (int i = 0; i < input.size(); ++i) {
            if (input[i] == '+' || input[i] == '-' || input[i] == '*') {
                vector<int> left = diffWaysToCompute(input.substr(0, i));
                vector<int> right = diffWaysToCompute(input.substr(i + 1));
                for (int j = 0; j < left.size(); ++j) {
                    for (int k = 0; k < right.size(); ++k) {
                        if (input[i] == '+') res.push_back(left[j] + right[k]);
                        else if (input[i] == '-') res.push_back(left[j] - right[k]);
                        else res.push_back(left[j] * right[k]);
                    }
                }
            }
        }
        if (res.empty()) res.push_back(stoi(input));
        return res;
    }
};

我们也可以使用 HashMap 来保存已经计算过的情况,这样可以减少重复计算,从而提升运算速度,以空间换时间,岂不美哉,参见代码如下:

解法二:


class Solution {
public:
    unordered_map<string, vector<int>> memo;
    vector<int> diffWaysToCompute(string input) {
        if (memo.count(input)) return memo[input];
        vector<int> res;
        for (int i = 0; i < input.size(); ++i) {
            if (input[i] == '+' || input[i] == '-' || input[i] == '*') {
                vector<int> left = diffWaysToCompute(input.substr(0, i));
                vector<int> right = diffWaysToCompute(input.substr(i + 1));
                for (int j = 0; j < left.size(); ++j) {
                    for (int k = 0; k < right.size(); ++k) {
                        if (input[i] == '+') res.push_back(left[j] + right[k]);
                        else if (input[i] == '-') res.push_back(left[j] - right[k]);
                        else res.push_back(left[j] * right[k]);
                    }
                }
            }
        }
        if (res.empty()) res.push_back(stoi(input));
        memo[input] = res;
        return res;
    }
};

当然,这道题还可以用动态规划 Dynamic Programming 来做,但明显没有分治法来的简单,但是既然论坛里这么多陈独秀同学,博主还是要给以足够的尊重的。这里用一个三维数组 dp,其中 dp[i][j] 表示在第i个数字到第j个数字之间范围内的子串添加不同括号所能得到的不同值的整型数组,所以是个三位数组,需要注意的是我们需要对 input 字符串进行预处理,将数字跟操作分开,加到一个字符串数组 ops 中,并统计数字的个数 cnt,用这个 cnt 来初始化 dp 数组的大小,并同时要把 dp[i][j] 的数组中都加上第i个数字,通过 ops[i*2] 取得,当然还需要转为整型数。既然 dp 是个三维数组,那么肯定要用3个 for 循环来更新,这里采用的更新顺序跟之前那道 Burst Balloons 是一样的,都是从小区间往大区间更新,由于小区间之前更新过,所以我们将数字分为两部分 [i, j] 和 [j, i+len],然后分别取出各自的数组 dp[i][j] 和 dp[j][i+len],把对应的运算符也取出来,现在又变成了两个数组中任取两个数字进行运算,又整两个 for 循环,所以总共整了5个 for 循环嵌套,啊呀妈呀,看这整的,看不晕你算我输,参见代码如下:

解法三:


class Solution {
public:
    vector<int> diffWaysToCompute(string input) {
        if (input.empty()) return {};
        vector<string> ops;
        int n = input.size();
        for (int i = 0; i < n; ++i) {
            int j = i;
            while (j < n && isdigit(input[j])) ++j;
            ops.push_back(input.substr(i, j - i));
            if (j < n) ops.push_back(input.substr(j, 1));
            i = j;
        }
        int cnt = (ops.size() + 1) / 2;
        vector<vector<vector<int>>> dp(cnt, vector<vector<int>>(cnt, vector<int>()));
        for (int i = 0; i < cnt; ++i) dp[i][i].push_back(stoi(ops[i * 2]));
        for (int len = 0; len < cnt; ++len) {
            for (int i = 0; i < cnt - len; ++i) {
                for (int j = i; j < i + len; ++j) {
                    vector<int> left = dp[i][j], right = dp[j + 1][i + len];
                    string op = ops[j * 2 + 1];
                    for (int num1 : left) {
                        for (int num2 : right) {
                            if (op == "+") dp[i][i + len].push_back(num1 + num2);
                            else if (op == "-") dp[i][i + len].push_back(num1 - num2);
                            else dp[i][i + len].push_back(num1 * num2);
                        }
                    }
                }
            }
        }
        return dp[0][cnt - 1];
    }
};

到此这篇关于c++实现LeetCode(241.添加括号的不同方式)的文章就介绍到这了,更多相关C++实现添加括号的不同方式内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C++实现LeetCode(241.添加括号的不同方式)

本文链接: https://www.lsjlt.com/news/130773.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c语言怎么计算字符串长度
    在C语言中,可以使用内置的strlen()函数来计算字符串的长度。该函数位于string.h头文件中。以下是一个示例代码: #inc...
    99+
    2024-05-14
    c语言
  • c语言怎么判断三个数的大小
    可以通过比较三个数之间的大小关系来判断它们的大小。以下是一个示例代码: #include <stdio.h> int ...
    99+
    2024-05-14
    c语言
  • c++排序函数sort怎么使用
    在C++中,使用sort函数对容器中的元素进行排序,可以按照默认的升序排列,也可以自定义排序规则。 下面是一个简单的例子,对一个ve...
    99+
    2024-05-14
    c++
  • c++中string的compare函数怎么使用
    在C++中,可以使用string的compare函数来比较两个string对象的大小。该函数的原型如下: int compare(c...
    99+
    2024-05-14
    c++
  • c++中string的compare函数有什么用
    c++中string的compare函数用于比较两个字符串的大小关系。该函数返回一个整数值,表示两个字符串的比较结果。 具体来说,如...
    99+
    2024-05-14
    c++
  • c++中print函数的功能有哪些
    在C++中,通常使用std::cout来进行输出操作,而不是使用print函数。std::cout是C++标准库中的一个输出流对象,...
    99+
    2024-05-14
    c++
  • c语言数据结构怎么使用
    在C语言中,数据结构可以通过定义结构体来实现。以下是一些常见的数据结构的使用方法示例: 链表: #include <std...
    99+
    2024-05-14
    c语言
  • c++中print函数的用法是什么
    在C++中没有内置的print函数,但可以使用cout来输出内容到控制台。cout是C++标准库中的一个输出流对象,可以使用<...
    99+
    2024-05-14
    c++
  • c++中concept的用法是什么
    在C++20中,Concept是一种新的语言特性,用于定义类型要求和约束。Concept可以被用来约束函数模板、类模板和普通函数的参...
    99+
    2024-05-14
    c++
  • c++中concept的作用是什么
    在C++中,concept的作用是定义一种通用的约束,用于限制模板参数的类型范围。通过使用concept,可以在编译时对模板参数进行...
    99+
    2024-05-14
    c++
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作