LeetCode Algorithms 241. Different Ways to Add Parentheses

本文介绍了一种解决给定算术表达式中不同计算顺序的方法。通过区间动态规划(DP)的方式,实现了对所有可能的计算顺序进行枚举并求解。详细解释了状态转移方程,并给出了具体的时间复杂度分析。

题目难度: Medium


原题描述:

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".
((2-1)-1) = 0
(2-(1-1)) = 2
Output: [0, 2]

Example 2
Input: "2*3-4*5"
(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
Output: [-34, -14, -10, -10, 10]


题目大意:

        给定一个包含数字和运算符的算术表达式,求通过在这个算术表达式添加括号所产生的全部可能的运算顺序得到的结果,运算符只有+、- 和 *。上面两个例子说得比较清楚,看完就基本懂题意了。


解题思路:

        这道题是通过分治的标签来找到的,所以解题方法应该就是分治,但是我的第一想法并不是分治,而是区间DP,在这里分享一下我的做法。其实做完后回想起来,感觉我区间DP的做法就是分治(递归,自上而下)的循环实现(自下而上),原理是一样的。

      总的思路如下:设dp[i][j]为从第i个数(从0开始)到第j个数构成的算术表达式的所有结果输出,则题目要求的结果为dp[0][len-1],其中len为算术表达式中全部数的个数。状态转移方程如下:dp[i][j] = dp[i][k] op dp[k+1][j] 的所有结果(做笛卡尔积),k>=i 且 k<j,op为在原来的算术表达式中第k个数(从0开始)后面跟的符号。

        程序的大致框架如下:程序的主体为diffWaysToCompute函数。这个函数包含几重for循环,最外层的for循环为遍历区间右端点和左端点的差d;里面一层循环为遍历在给定区间差时所有可能的区间;再里面一层循环为遍历在给定区间时将其分成两个子区间的所有可能性;最里面的两层循环是用于计算在给定两个子区间后这两个子区间做运算的所有结果。按照这样的顺序计算是因为每个区间的计算要用到其分成所有可能的两个子区间的结果。

      此外,extractNumAndOp函数用于将输入字符串的数字和运算符分别提取出来,calFormula函数用于计算表达式的值。


时间复杂度分析:

        设算术表达式包含n个数,则最外面三层循环每一层都是O(n)的复杂度,因此外面三层循环的复杂度为O(n^3),里面两层循环为给定两个子区间做运算的所有结果,复杂度应该是O(n^2)。因此总的复杂度为O(n^5)?


以下是代码:

const int maxLen = 500;
vector<int> dp[maxLen][maxLen];

class Solution {
public:
    void init()
{
    for(int i=0 ; i<maxLen ; ++i){
        for(int j=0 ; j<maxLen ; ++j){
            dp[i][j].clear();
        }
    }
}

//将输入字符串的数字和运算符分别提取出来
void extractNumAndOp(string input , vector<int> & number , vector<char> & op)
{
    int sum = 0;
    for(int i=0 ; input[i]!='\0' ; ++i){
        if(isdigit(input[i])){
            sum = sum*10+(input[i]-'0');
        }
        else{
            number.push_back(sum);
            sum = 0;
            op.push_back(input[i]);
        }
    }
    number.push_back(sum);
}

int calFormula(int x , int y , char op)
{
    int sum = 0;
    switch(op)
    {
    case '+':
        sum = x+y;
        break;
    case '-':
        sum = x-y;
        break;
    case '*':
        sum = x*y;
        break;
    default:
        break;
    }
    return sum;
}

vector<int> diffWaysToCompute(string input)
{
    init();
    vector<int> number;
    vector<char> op;
    extractNumAndOp(input,number,op);
    int len = number.size();

    for(int i=0 ; i<len ; ++i){
        dp[i][i].push_back(number[i]);
    }

    for(int d=1 ; d<len ; ++d){
        for(int i=0 ; i<len-d ; ++i){
            //计算dp[i,i+d]
            for(int j=i ; j<i+d ; ++j){
                //计算dp[i][j] op dp[j+1][i+d],笛卡尔运算
                for(int x=0 ; x<dp[i][j].size() ; ++ x){
                    for(int y=0 ; y<dp[j+1][i+d].size() ; ++y){
                        dp[i][i+d].push_back( calFormula(dp[i][j][x], dp[j+1][i+d][y], op[j]) );
                    }
                }
            }
        }
    }
    sort(dp[0][len-1].begin() , dp[0][len-1].end());
    return dp[0][len-1];
}
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值