前言
用了一个小时,想了一种O(n)的算法,结果发现才超过百分之十几的人。。。但是后来看了solution,基本也都是这种复杂度呀,为啥我的运行的这么慢呢?可能是代码写的太烂了吧,又参考了别人的思想,写了动态规划的解法,如果有时间就写下来,大家也可以去看官方给的解法。
问题描述
解法一
下面的解法就是我的垃圾解法,首先通过一个list来实现堆栈,对整个数组进行遍历,找到所有可以配对的括号,记录在另一个list中,然后再遍历另一个list,找到连续为1的最长的段就是结果啦!
class Solution:
def longestValidParentheses(self, s: str) -> int:
if s == None:
return
stac = []
count = 0
ce = [0]*len(s)
for k in s:
if len(stac) == 0 :
stac.append([count,k])
else :
if stac[-1][1]=='(' and k == ')':
ce[stac[-1][0]] = 1 #如果能配对则标记为1
ce[count] = 1
stac.pop()
else:
stac.append([count,k])
count +=1
maxlen = 0
curr = 0
for k in ce:
if k == 1:
curr +=1#如果是1则长度加1
else:
if curr>maxlen:#否则检查是否是最长的
maxlen = curr
curr = 0#长度置零 重新找连续的长度
if curr>maxlen:#有可能最后一个也是符合条件的
maxlen = curr
return maxlen
如果没有看明白,那就看一下ce 的输出值就明白我的意思了:
这个解法就是遍历两遍字符串,时间复杂度和空间复杂度都是O(n),如果是c语言之类的,标志位可以直接赋值在原字符串中,而不用重新申请一个list了。。。但是运行速度就是慢,可能是中间有很多的判断吧。。。
解法二
DP解法,说实话,这个题看到它的第一眼我就想到了动态规划,但是我尝试规划失败了(-_-),感觉分析的有点乱,一直没有弄清楚其中的关系。直到看到了官方给的DP算法,才算大致明白如何规划了吧。这里就说一下我简单的理解吧!
首先定义d(n)代表以第n个字符结尾的最长字符串的长度,因为只有 ‘)’ ,才算是合法的结尾,而 ‘(’ 结尾的最长字符串长度应该是零。所以先假设d全部为零,然后逐个加入字符。
在已知d(n-1)的情况下,求第n个字符加入进来后当前字符串的最长长度。
下面讨论几种情况:
如果s(n)=’(’:不用讨论,d(n) = 0(默认)
否则:
1>s(n)=’)’ 并且 s(n-1) = '('
此时最后两个字符构成一个合法的括号,则以当前字符s(n)结尾最长的长度为d(n-2)+2
2>s(n)=’)’ 并且 s(n-1) = ')'
这个时候如果s(n)=’)’ 为结尾构成了一个合法结尾,那么在前面必须有一个 ‘(’ 与之对应,而且这个 '('的位置肯定得在n-d(n-1)-1的位置上,如下图所示:
那么此时以s(n)=’)’ 为结尾的d(n) = d(n-d(n-1)-2) + 1 + d(n-1) +1 = d(n-d(n-1)-2) + d(n-1) + 2
由此全部情况都讨论完了,中间写代码时需要注意初始值的判断即可,我的代码如下,里面有些注释,看不懂的可以问我!
class Solution:
def longestValidParentheses(self, s: str) -> int:
if s == None:
return
maxlen = 0
d = [0]*len(s)
for k in range(1,len(s),1):
if s[k] == ')':#只处理最后一个字符是')',因为只有这样才会是一个合法的结尾
if s[k-1] == '(':#如果是()则加2
if k>=2:
d[k] = d[k-2]+2
else:
d[k] = 2
else:#如果是)) 则要判断d[s-1]个字符前是否是'('
if k>d[k-1] and s[k-1-d[k-1]]=='(':#如果是 则说明从第k-d[s-1]-1到最后一个构成合法字符串
if k-d[k-1]-2>=0:
d[k]= d[k-d[k-1]-2] + d[k-1] + 2 # 长度二是[k-d[s-1]-1,k]这两个字符
else:
d[k] = d[k-1] + 2
if d[k]>maxlen:
maxlen = d[k]
return maxlen
这个算法一下子超过了99.95%的人,哇咔咔,看来O(n)和O(n)还是有一定的差距的 ! 学习到了。。
解法三
这个解法是看的官方给的代码,类似于双指针,先从前往后,累计计算左括号和右括号的数量。
如果两者相等则说明此时构成了合法子串,判断其长度(等于左右括号的数量)是否大于最大长度,如果大,则更新。
如果出现右括号数量比左括号数量多的,则说明出现了非法的子串,此时重新开始计数左括号和右括号数量,直到遍历整个字符串。
最后再从后往前遍历一遍,找到最长的长度值即可。
下面给出代码实现,实际上就是官方的代码实现,而且还是java实现的,我就不改成python版本的了,直接拿来用了,这里给出代码链接!
public class Solution {
public int longestValidParentheses(String s) {
int left = 0, right = 0, maxlength = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
left++;//如果找打左括号 左括号计数加
} else {
right++;//右括号计数加
}
if (left == right) {
maxlength = Math.max(maxlength, 2 * right);//更新最大长度值
} else if (right >= left) {
left = right = 0;//重新开始计数
}
}
//倒着重新遍历一遍
left = right = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if (s.charAt(i) == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = Math.max(maxlength, 2 * left);
} else if (left >= right) {
left = right = 0;
}
}
return maxlength;
}
}
总结
唉,做题用了一个小时,写这个博客用了近两个小时,虽然很费时,虽然网上也有很多人写这样的博客,虽然这样很像造轮子,但是这样真的对题目会有更多的理解,也算是一种提升吧,不能只闷头刷题,要适当的有点总结!希望自己能够坚持下去吧!
最后送上我比较喜欢的一句话,共同勉励!