leetcode 第32题:动态规划思想与堆栈的灵活使用

题目大意:

  给定一个只用’(‘和’)’组成的字符串,输入其最长合法成对子串的长度。

示例:

  输入:(()),则应当输出整数4;输入:(()()),则应当输出整数6;输入:((())),应当输出6。
 

解题思路:

  之所以一下跳了这么多题,是因为之前发现自己在做动态规划类型的题目方面比较薄弱,因此特地按类型搜索题目,挑选了这一道题练手。
  这道题本身是hard级别的难度。首先,必须明确了暴力搜索法肯定是行不通的。其次,在思考如何解决这一问题时,基于这是一道动态规划的题目,因此我想套用一下前一道题的做法。先找出字符串中的”()”子字符串,然后将其左右扩展至最大,将最大长度保留。但这个方案仔细思考后发现也行不通。一是复杂度并不低,也有O(n^2),其次左右扩展时情况相对复杂,难以用程序表示。
  在参考了答案之后方才恍然大悟。解答中给出了两种方案,一种是利用动态规划的思想去解决,另一种则利用了堆栈的特性。
  第一种方案中,将字符串从左至右依次遍历,用数组dp记录遍历到每一个字符处时,所在子串的当前最大合法长度。首先将dp全部置零。遍历过程中,如果某处字符是’)’,并且前一个字符是’(‘,则此处的dp值应当为它向前数两个位置的dp值加二;如果前一个字符是’)’并且当前字符对称位置上的字符是’(‘,则说明成对,就应当在前一个字符的dp值的基础上加2,同时还要把之前单独成对的串的长度算进去。
  啰啰嗦嗦说了一堆,配合动画演示更容易理解。具体可以见:https://leetcode.com/problems/longest-valid-parentheses/solution/
  这套思路,明白之后很清晰,但要自己思考出来的确很难。在我看来,这套解法的关键,就是缕清了遍历过程中合法字符串的构成。有几个关键的结构,一是”(()())”,二是”((()))”,三是它们的连接”(()())((()))”,弄清楚程序在遍历这几个字符串过程中的行为就好理解多了。换个角度来说,就是如何以dp数组为基础建立动态规划中的地推关系。这一过程,需要反复实践与思考才能得到解答。
  答案中还给出了使用堆栈解决的方案。这主要是利用了堆栈先进后出的特性。一般的想法当然是将符号入栈,这么做存在一个问题:无法确定遍历过程中的最长合法字符串长度。答案给出的方法则是将字符对应的下标入栈。这样,直接对栈内元素进行计算就容易得出结果。具体过程不再赘述,可以见如上的链接。
  用第一种方法AC的代码如下:
class Solution {
public:
int longestValidParentheses(string s) {
int len=s.length(),maxans=0;
vector<int> dp;
dp.push_back(0);
for(int i=1;i<len;i++){
if(s.at(i)=='(')
dp.push_back(0);
if(s.at(i)==')') //如果是右括号,则要记录成对括号的数目
{
if(s.at(i-1)=='(') //如果是一重成对的括号,则在原来的基础上加上2
{
dp.push_back((i>=2?dp[i-2]:0)+2);
}
else if((i>=dp[i-1]+1)&&s.at(i-dp[i-1]-1)=='(') //否则比较其对称位置符号,如果是'(',则说明成对,则在原来的基础上更新dp值
{
dp.push_back(dp[i-1]+((i>dp[i-1]+2)?dp[i-dp[i-1]-2]:0)+2);
}
else
dp.push_back(0);
maxans=max(maxans,dp[i]);
}
}
return maxans;
}
};
  
  
  

猜你喜欢

转载自blog.csdn.net/xbb123456rt/article/details/77773787
今日推荐