第一道
题目名称:32. Longest Valid Parentheses
题目难度:Hard
题目描述:Given a string containing just the characters (
and )
, find the length of the longest valid (well-formed) parentheses substring.
For (()
, the longest valid parentheses substring is ()
, which has length = 2.
Another example is )()())
, where the longest valid parentheses substring is ()()
, which has length = 4.
题目分析:
题目要求我们找到最长的合法的子串,所谓的合法就是()
左右括号要符合匹配规则。
一开始觉得挺简单的,就是用一个stack去处理。遍历一次待检测的字符串,遇到(
就放进栈中,遇到)
就检查栈中有没有(
,如果有就弹出然后计入长度+1,没有就直接处理下一个。
但是实际上这样做计算得到的是整个字符串中所有左右括号匹配的个数,并不是题目要求,题目要求这些有效的匹配是必须连在一起的。
如())()
,结果应该是2,而不是4。
于是我改进了算法,先用那些多出来的符号将原字符串分成多个有效的子字符串,然后取出这些子字符串中长度最长的一个即可。
最后AC的算法是:
class Solution {
public:
struct node {
int index;
char value;
node(int i, char c) {
index = i;
value = c;
}
};
int getLength(string s) {
stack<char> stack;
int count = 0;
for (int i = 0; i < s.size(); ++i) {
if (s[i] == '(') {
stack.push(s[i]);
} else if (s[i] == ')') {
if (!stack.empty()) {
if (stack.top() == '(') {
stack.pop();
count += 2;
}
}
}
}
return count;
}
int longestValidParentheses(string s) {
stack<node*> s1;
int count = 0;
bool flag = 0;
for (int i = 0; i < s.size(); ++i) {
node *temp = new node(i, s[i]);
if (s[i] == '(') {
s1.push(temp);
} else if (s[i] == ')') {
if (s1.empty()) s1.push(temp);
else if (!s1.empty()) {
if (s1.top()->value == '(') {
s1.pop();
count += 2;
} else {
s1.push(temp);
}
}
}
}
if (s1.empty()) return count;
int from = 0;
int nc = 0;
stack<int> s2;
while (!s1.empty()) {
node *temp = s1.top();
s1.pop();
s2.push(temp->index);
}
while (!s2.empty()) {
int to = s2.top()-from;
int t;
if (to == -1) {
t = getLength("");
} else {
string sub = s.substr(from, to);
cout << s2.top() << endl;
cout << sub << endl;
t = getLength(sub);
}
from = s2.top();
s2.pop();
nc = max(nc, t);
}
string sub = s.substr(from, s.size()-from);
int t = getLength(sub);
nc = max(nc, t);
return nc;
}
};
但是其实这个算法的第二部分有点啰嗦,改进版是:
class Solution {
public:
int longestValidParentheses(string s) {
int n = s.length(), longest = 0;
stack<int> st;
for (int i = 0; i < n; i++) {
if (s[i] == '(') st.push(i);
else {
if (!st.empty()) {
if (s[st.top()] == '(') st.pop();
else st.push(i);
}
else st.push(i);
}
}
if (st.empty()) longest = n;
else {
int a = n, b = 0;
while (!st.empty()) {
b = st.top(); st.pop();
longest = max(longest, a-b-1);
a = b;
}
longest = max(longest, a);
}
return longest;
}
};
第二道
题目名称:42. Trapping Rain Water
题目难度:Hard
题目描述:Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1]
, return 6.
题目分析:
题目给出一系列的长度,我们可以看成是这一系列长度构成了一个水缸,要求我们找到这个水缸可以装的水的最大容量。
思路是扫面一遍,每次找出两条边,作为容器的两侧,然后计算这个子容器可以装多少水,然后从这个子容器的右侧边开始继续扫描,找出一个新的容器。这样,扫完之后,就得到了整个容器的容量大小。
需要注意的是,从当前左侧开始,怎么确定子容器的右侧呢?有两种情况,第一种情况就是找到第一条不小于左侧的边作为右侧边,第二种情况是右边的所有边都小于左侧的边,这时候应该找出右侧边中最大的边来作为子容器的右侧。
最后AC的代码是:
class Solution {
public:
int trap(vector<int>& height) {
int total = 0;
for (int i = 0; i < height.size(); ++i) {
int highest = -1;
int index = i;
int flag = 0;
//寻找不小于左侧的边作为右侧边
for (int j = i + 1; j < height.size(); ++j) {
if (height[j] >= height[i]) {
flag = 1;
index = j - 1;
break;
}
}
//若没有,则寻找右边中长度最大的边作为右侧边
if (!flag) {
for (int j = i + 1; j < height.size(); ++j) {
if (height[j] > highest) {
highest = height[j];
index = j - 1;
}
}
}
//计算子容器的容量大小
int width = index - i + 2;
int hei = min(height[i], height[index + 1]);
int area = width * hei;
int realArea = area - hei * 2;
for (int k = i + 1; k <= index; ++k) {
realArea -= height[k];
}
total += realArea;
//当前容器的右侧边作为下一子容器的左侧边
i = index;
}
return total;
}
};