题目描述

image.png

解题思路

dp

dp[i]的含义:在s字符串中索引为i的字符匹配的最长有效括号字串。
递推公式:

  • 如果s[i] = ‘)’ , s[i-1] = ‘(‘ , dp[i] = dp[i - 2] + 2;
  • 如果s[i] = ’)‘, s[i - 1] = ‘)’ , dp[i - dp[i - 2] - 1] = ‘(‘, dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2

解释一下:如果当前是 ) ,前一个字符是 ( ,此时就可以匹配,所以为dp[i-2] + 1。
第二个如何推出来的?如果当前字符和前一个字符都是 )) ,此时我们需要考虑是不是中间都为匹配过的,我们要找到有没有和我们 ) 这个匹配的 ( ,所以需要判断 dp[i - dp[i - 2] - 1](注意减去中间匹配的进行判断) 是否为 ( ,如果不是,那也不匹配,如果是,那么 dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2 。
为啥是3部分呢?
dp[i - 1] 表示中间匹配的,dp[i - dp[i - 1] - 2] 表示dp[i - dp[i - 1] - 1]前面匹配的子串有多少(没有匹配的就为0),2是刚刚判断的2个字符。
代码如下:注意判断和防止数组越界。
动图参照官解法:https://leetcode.cn/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/

  1. // dp
  2. // 如果s[i] = ')' , s[i-1] = '(' , dp[i] = dp[i - 2] + 2;
  3. // 如果s[i] = ’)‘, s[i - 1] = ')' , dp[i - dp[i - 2] - i] = '(', dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2
  4. public int longestValidParentheses(String s) {
  5. int maxAns = 0;
  6. int[] dp = new int[s.length()];
  7. char[] array = s.toCharArray();
  8. for (int i = 1; i < array.length; i++) {
  9. if (array[i] == ')') {
  10. if (array[i - 1] == '(') {
  11. dp[i] = (i - 2 >= 0 ? dp[i - 2] : 0) + 2;
  12. } else if (i - dp[i - 1] > 0 && array[i - dp[i - 1] - 1] == '(') {
  13. dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
  14. }
  15. }
  16. maxAns = Math.max(maxAns, dp[i]);
  17. }
  18. return maxAns;
  19. }

image.png

image.png
动图参照官解法:https://leetcode.cn/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
很巧妙的做法:存储的是下标,栈底始终为最后一个没有被匹配的的右括号的下标,那么我们在匹配之后根据栈是否为空来判断,如果为空,那么此时没有匹配的,所以我们需要放入右括号的索引在栈底,如果不为空,那么可以直接求匹配的最大子串。

  1. // 栈
  2. public int longestValidParentheses(String s) {
  3. // 存储下标,栈底始终为最后一个没有被匹配的的右括号的下标
  4. Stack<Integer> stack = new Stack<>();
  5. // 初始化-1进去,满足一开始栈为空的情况
  6. stack.push(-1);
  7. int res = 0;
  8. char[] array = s.toCharArray();
  9. for (int i = 0; i < array.length; i++) {
  10. if (array[i] == '(') {
  11. stack.push(i);
  12. } else if (array[i] == ')') {
  13. // 表示匹配
  14. stack.pop();
  15. if (stack.isEmpty()) {
  16. stack.push(i);
  17. } else {
  18. res = Math.max(res, i - stack.peek());
  19. }
  20. }
  21. }
  22. return res;
  23. }

image.png

贪心优化空间复杂度为O(1)

image.png
动图参照官解法:https://leetcode.cn/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
向右遍历,进行匹配,但会丢失(()的情况,而()))不会丢失,因为右括号大于左括号会直接重置。所以还需要向左遍历一次。

  1. // 不开辟额外的空间
  2. public int longestValidParentheses(String s) {
  3. int left = 0, right = 0, maxLength = 0;
  4. for (int i = 0; i < s.length(); i++) {
  5. if (s.charAt(i) == '(') left++;
  6. else right++;
  7. if (left == right) maxLength = Math.max(maxLength, 2 * right);
  8. else if (right > left) left = right = 0;
  9. }
  10. left = right = 0;
  11. for (int i = s.length() - 1; i > 0; i--) {
  12. if (s.charAt(i) == '(') left++;
  13. else right++;
  14. if (left == right) maxLength = Math.max(maxLength, 2 * left);
  15. else if (left > right) left = right = 0;
  16. }
  17. return maxLength;
  18. }