Leetcode算法学习日志-241 Different Ways to Add Parentheses

来源:互联网 发布:java流行框架2017 编辑:程序博客网 时间:2024/06/06 02:54

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 *. Input "2-1-1", out put [0,2].

题意分析

输入为一个字符串,其中包含相间隔的数字及运算符,通过不同的括号添加方法可以得到不同的解,如果括号添加方法不同,解相同,也应该将相同的解输出。由于总有一个运算符要在最后算,所以可以将问题划分为此运算符两边子字符串的加括号问题,采用递归的方法,最终由子问题得到原问题。

解法分析

本题选用的编程语言为C++,用了以下两种方法:

  • Divide and Conquer
  • Dynamic Programming

Divide and Conquer

此题的分治法思想和前几题稍有不同,前面53,169均是分成大小相等的两个子问题,分别求解再组合,而本题是根据运算符的位置将原字符串分成两个部分,长度不相等,且原问题通过一次分解不能得到解决,需要遍历所有的运算符,也就是说递归过程在循环内部,所以说,这是一个典型的递归问题,但由于分治和递归在概念上是一种孪生关系,所以说这一题用了分治思想也未尝不可。由于不能写出递归式,所以本题不能利用主方法求复杂度。C++代码如下:

class Solution {public:    vector<int> diffWaysToCompute(string input) {        vector<int> res;        int n=input.size();        int i;        vector<int> lRes;        vector<int> rRes;        for(i=0;i<n;i++){            if(input[i]=='+'||input[i]=='-'||input[i]=='*'){                lRes=diffWaysToCompute(input.substr(0,i));                rRes=diffWaysToCompute(input.substr(i+1));                for(auto n1:lRes){                    for(auto n2:rRes){                        if(input[i]=='+')                            res.push_back(n1+n2);                        if(input[i]=='-')                            res.push_back(n1-n2);                        if(input[i]=='*')                            res.push_back(n1*n2);                    }                }            }        }        if(res.empty())            res.push_back(atoi(input.c_str()));        return res;    }};
设原问题的时间复杂度为T(n),则T(n)可以写为:

\begin{array}{l}T(n) = T(1) + T(n - 1) + T(2) + T(n - 2) +  \ldots  + T(n - 1) + T(1) = 2(T(1) +  \ldots  + T(n - 1))\\T(n + 1) = 2(T(1) +  \ldots T(n)) = 3T(n)\\T(n) = O({3^n})\end{array}

 if(res.empty())            res.push_back(atoi(input.c_str()));
代码中这一段用来作为递归的终点,也就是当res为空,输入input只是一个数字字符时,应该直接返回这个数字字符对应的整型数。

代码中运用到的C++知识:

  • int atoi(const char *uptr),是将一个字符串转化为一串整型数,输入为字符串的首地址,由于C++中String是一个封装类,所以需要利用其成员函数c_str()来获得一个常首地址。

Dynamic Programming

之前提到过动态规划一般用来解决最优化问题,然而更一般地说,动态规划可以用来解决需要遍历各种可能情况的问题。本题利用递归思想解决时,有许多子问题是重叠的,对这些相同问题每次都进行求解是很费时的,所以可以将已求得的子问题保存,这道题可以保存在关联容器中。C++代码如下:

class Solution {public:    vector<int> diffWaysToCompute(string input) {        int n=input.size();        int i;        vector<int> lRes;        vector<int> rRes;        vector<int> res;        string lStr;        string rStr;        if(!myMap[input].empty())            return myMap[input];        else{            for(i=0;i<n;i++){              if(input[i]=='+'||input[i]=='-'||input[i]=='*'){                lStr=input.substr(0,i);                rStr=input.substr(i+1);                lRes=diffWaysToCompute(lStr);                rRes=diffWaysToCompute(rStr);                for(auto n1:lRes){                    for(auto n2:rRes){                        if(input[i]=='+')                           res.push_back(n1+n2);                        if(input[i]=='-')                           res.push_back(n1-n2);                        if(input[i]=='*')                           res.push_back(n1*n2);                        }                    }               }                   }            }        if(res.empty()){            res.push_back(atoi(input.c_str()));            return res;        }                myMap[input]=res;        return res;       }private:    unordered_map<string,vector<int>> myMap;};
每进入一次此函数都会先判断输入字符串对应的可能结果是不是已经求出过,如果求出过,则直接用该字符串作为关键字返回在关联容器myMap中存储的值,反之就进入普通求解过程。此算法时间复杂度远低于迭代方法。

上述代码用到的C++知识如下:

  • 变量的作用域和生存时间。变量的作用域决定于其所定义的位置,如果在函数内部定义,则其只在函数内部有效,如果在函数外部定义,则在函数内部可见,但如果在函数内部定义同名变量,则函数内部定义的变量优先级更高。对于局部变量,在函数返回后变量消亡,如果局部变量为Static类型,则其生命周期为整个程序运行周期,但它的作用域仍然只在函数内部。
  • 全局变量和静态全局变量:全局变量和静态变量都是采用静态存储的,所以全局变量和静态全局变量的生命周期一样,都是整个程序运行周期,不同的是它们的作用域,静态局部变量作用域只有这个源文件,而全局变量可以用于一个头文件下的多个源文件。
  • java中由于只有类,所以不存在全局和局部的概念,变量的作用域决定于是private、public还是protect,而在方法中定义的变量作用域只在该方法中。




原创粉丝点击