241为运算表达式设计优先级

╰+攻爆jí腚メ 2023-02-28 01:30 119阅读 0赞

一、前言

分类:Divide and Conquer。

问题来源LeetCode 241 难度:中等。

问题链接:https://leetcode-cn.com/problems/different-ways-to-add-parentheses

二、题目

给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。你需要给出所有可能的组合的结果。有效的运算符号包含 +, - 以及 * 。

示例1:

  1. 输入: "2-1-1"
  2. 输出: [0, 2]
  3. 解释:
  4. ((2-1)-1) = 0
  5. (2-(1-1)) = 2

示例 2:

  1. 输入: "2*3-4*5"
  2. 输出: [-34, -14, -10, -10, 10]
  3. 解释:
  4. (2*(3-(4*5))) = -34
  5. ((2*3)-(4*5)) = -14
  6. ((2*(3-4))*5) = -10
  7. (2*((3-4)*5)) = -10
  8. (((2*3)-4)*5) = 10

三、思路

这里提供两种解决方法。

方法一:分治法。以操作符为界限,将运算表达式分割为两个不同的部分,在递归计算(在这个运算符处分割,其实也是代表将这个运算符最后一个算)递归终止条件:字符串中就一个数字在递归的过程中,会有一部分字符串会重复多次计算,所以在前面的基础上加入哈希表,同空间换时间,执行速度会快些。

方法二:动态规划。首先将字符串中的数字和操作符分别存储下来第i个操作符对应的数字是i和i+1,同理第i个数的前面的操作符是i-1,后面的一个是i(i表示在数组中的序号)dp[i][j]表示从第i个数字到第j个数字的所有情况

(1)i==j 等于数字本身的值

(2)i != j(j肯定是大于i的)

将i-j分成两个式子来看,[i,i]op[i+1,j],[i,i+1]op[i+2,j]…[[i,j-1]]op[j,j]

将上面的所有情况全部组合起来。[i,i+k]op[i+k+1,j],op是ops数组里面的ops[i+k]。

有了以上,我们就可以写出动态规划了,还有一个需要注意的地方是,(2)情况也就是一个遍历,但是遍历的顺序需要注意,不应该是[0,j]->[j-1,j]而应该是[j-1,j]->[0->j]。如果是从[0,j]开始,你会发现[1,j]..[j-1,j]这些你需要的都还没算。

下面是另外一种理解说明,思路和上面一样。

20200718233115962.png

四、编码实现

  1. //==========================================================================
  2. /*
  3. * @file : 241_DiffWaysToCompute.h
  4. * @label : Divide and Conquer
  5. * @blogs : https://blog.csdn.net/nie2314550441/article/details/107437244
  6. * @author : niebingyu
  7. * @date : 2020/07/18
  8. * @title : 241.为运算表达式设计优先级
  9. * @purpose : 给定一个含有数字和运算符的字符串,为表达式添加括号,改变其运算优先级以求出不同的结果。
  10. * 你需要给出所有可能的组合的结果。有效的运算符号包含 +, - 以及 * 。
  11. *
  12. * 示例1:
  13. * 输入: "2-1-1"
  14. * 输出: [0, 2]
  15. * 解释:
  16. * ((2-1)-1) = 0
  17. * (2-(1-1)) = 2
  18. *
  19. * 示例2:
  20. * 输入: "2*3-4*5"
  21. * 输出: [-34, -14, -10, -10, 10]
  22. * 解释:
  23. * (2*(3-(4*5))) = -34
  24. * ((2*3)-(4*5)) = -14
  25. * ((2*(3-4))*5) = -10
  26. * (2*((3-4)*5)) = -10
  27. * (((2*3)-4)*5) = 10
  28. *
  29. * 来源:力扣(LeetCode)
  30. * 难度:中等
  31. * 链接:https://leetcode-cn.com/problems/different-ways-to-add-parentheses
  32. */
  33. //==========================================================================
  34. #pragma once
  35. #include <iostream>
  36. #include <string>
  37. #include <vector>
  38. #include <algorithm>
  39. #include <assert.h>
  40. using namespace std;
  41. #define NAMESPACE_DIFFWAYSTOCOMPUTE namespace NAME_DIFFWAYSTOCOMPUTE {
  42. #define NAMESPACE_DIFFWAYSTOCOMPUTEEND }
  43. NAMESPACE_DIFFWAYSTOCOMPUTE
  44. /*
  45. 分治归并
  46. 以操作符为界限,将运算表达式分割为两个不同的部分,在递归计算(在这个运算符处分割,其实也是代表将这个运算符最后一个算)
  47. 递归终止条件:字符串中就一个数字
  48. 在递归的过程中,会有一部分字符串会重复多次计算,所以在前面的基础上加入哈希表,同空间换时间,时间确实快了,从12ms->4ms
  49. */
  50. // 方法一,分治法
  51. class Solution_1
  52. {
  53. public:
  54. vector<int> diffWaysToCompute(string input)
  55. {
  56. int index = 0;
  57. int num = 0;
  58. while(index < input.size() && isdigit(input[index]))
  59. num = num * 10 + input[index++] - '0';
  60. if(index == input.size())
  61. {
  62. hash[input] = { num };
  63. return { num };
  64. }
  65. vector<int> ans;
  66. for(int i = 0; i < input.size(); i++)
  67. {
  68. if(isOp(input[i]))
  69. {
  70. string s1 = input.substr(0, i);
  71. string s2 = input.substr(i);
  72. vector<int> result1, result2;
  73. if(!hash.count(s1))
  74. result1 = diffWaysToCompute(input.substr(0, i));
  75. else
  76. result1 = hash[s1];
  77. if(!hash.count(s2))
  78. result2 = diffWaysToCompute(input.substr(i+1));
  79. else
  80. result2 = hash[s2];
  81. for(int r1 : result1)
  82. {
  83. for(int r2 : result2)
  84. {
  85. ans.push_back(calculate(r1, input[i] ,r2));
  86. }
  87. }
  88. }
  89. }
  90. hash[input] = ans;
  91. return ans;
  92. }
  93. bool isOp(const char& c)
  94. {
  95. return c == '+' || c == '-' || c == '*';
  96. }
  97. int calculate(const int& num1, const char& op, const int& num2){
  98. if(op == '+')
  99. return num1 + num2;
  100. else if(op == '-')
  101. return num1 - num2;
  102. else
  103. return num1 * num2;
  104. }
  105. private:
  106. unordered_map<string,vector<int>> hash;
  107. };
  108. // 方法二,动态规划
  109. class Solution_2
  110. {
  111. public:
  112. vector<int> diffWaysToCompute(string input)
  113. {
  114. vector<int> nums;
  115. vector<char> ops;
  116. int num = 0;
  117. for(int i = 0; i < input.size(); i++)
  118. {
  119. if(isOp(input[i]))
  120. {
  121. nums.push_back(num);
  122. ops.push_back(input[i]);
  123. num = 0;
  124. }
  125. else
  126. num = num * 10 + input[i] - '0';
  127. }
  128. nums.push_back(num);
  129. int n = nums.size();
  130. vector<vector<vector<int>>> dp(n,vector<vector<int>>(n));
  131. for(int i = 0; i < n; i++)
  132. dp[i][i].push_back(nums[i]);
  133. for(int j = 1; j < n; j++)
  134. {
  135. for(int i = j-1; i >= 0; i--)
  136. {
  137. for(int k = i; k < j; k++)
  138. {
  139. for(int r1 : dp[i][k])
  140. {
  141. for(int r2 : dp[k+1][j])
  142. {
  143. dp[i][j].push_back(calculate(r1,ops[k],r2));
  144. }
  145. }
  146. }
  147. }
  148. }
  149. return dp[0][n-1];
  150. }
  151. bool isOp(const char& c)
  152. {
  153. return c == '+' || c == '-' || c == '*';
  154. }
  155. int calculate(const int& num1, const char& op, const int& num2){
  156. if(op == '+')
  157. return num1 + num2;
  158. else if(op == '-')
  159. return num1 - num2;
  160. else
  161. return num1 * num2;
  162. }
  163. };
  164. 以下为测试代码//
  165. // 测试 用例 START
  166. void test(const char* testName, string input, vector<int> expect)
  167. {
  168. Solution_1 s1;
  169. vector<int> result1 = s1.diffWaysToCompute(input);
  170. sort(result1.begin(), result1.end());
  171. Solution_2 s2;
  172. vector<int> result2 = s2.diffWaysToCompute(input);
  173. sort(result2.begin(), result2.end());
  174. if (result1 == expect && result2 == expect)
  175. cout << testName << ", solution passed." << endl;
  176. else
  177. cout << testName << ", solution failed. " << endl;
  178. }
  179. // 测试用例
  180. void Test1()
  181. {
  182. string input = "2*3-4*5";
  183. vector<int> expect = { -34, -14, -10, -10, 10 };
  184. test("Test1()", input, expect);
  185. }
  186. NAMESPACE_DIFFWAYSTOCOMPUTEEND
  187. // 测试 用例 END
  188. //
  189. void DiffWaysToCompute_Test()
  190. {
  191. cout << "------ start 241.为运算表达式设计优先级 ------" << endl;
  192. NAME_DIFFWAYSTOCOMPUTE::Test1();
  193. cout << "------ end 241.为运算表达式设计优先级 --------" << endl;
  194. }

执行结果:

20200718233232795.png

发表评论

表情:
评论列表 (有 0 条评论,119人围观)

还没有评论,来说两句吧...

相关阅读