最长合法匹配子串长度(动态规划解法)
简述
- 给定一个只包含"(",")"的字符串,是判断一个最长的且能完全匹配的子串的长度。看到这种最有解问题,本能应该想到是动态规划解题。(且只求最值不求序列的可以递归实现)这是LeetCode动态规划类别的一道题。
-
问题描述
给定一个字符串,只包含"(“和”)“,规定一个”(“必须匹配一个”)“,找一个最长的可以完全匹配的子串长度。如”((()“答案为2,”(()))"答案为4。
问题分析
如何找到最优子结构以及状态转移函数和边界是动态规划的核心思路。如何用已知最优解去构造当前最优解,这就是这题的思路。很明显,这是要填一个一维表。
不妨定义F(n)为以第n个字符为结尾的最大子串的长度。可以简单分析一下两种情况。
- 第n个字符是"(“,不存在某个匹配子串结尾是”(",dp记为0。
- 第n个字符是")",此时需要看前面一个字符。
- 若s[n-1]为"(",那么两者刚好匹配,最长长度为这两者的长度2加上n-2为结尾的最长子串长度。(简单理解,刚好衔接)
- 若s[n-1]为")“,那么就要去看以这个右括号为结尾的最长子串的前一个字符j是什么,如果j是”(“,那么刚好,这个”(“和n位置上的”)“包裹了一个最长子串,这是最长子串就是包裹的这个子串加以包裹起始的”(“前一个字符为结尾的字符串长度,此长度为2+dp[n-1]+dp[n-1-dp[n-1]-1];如果j是”)",衔接断开,dp[n]保持0即可。
F(n)边界、状态转移函数如下。
- F(n) = 0 ,s[n]=“(”
- F(n) = F(n-2) + 2 ,s[n-1]=“(”
- F(n) = 0 ,s[n-1]=“)”,s[i-1- dp[i - 1]]=“)”
- F(n) = 2 + F[i - 1] + F[i-1-dp[i-1]-1] ,s[n-1]=“)”,s[i-1- dp[i - 1]]=“(”
代码
注意:Python的列表可以负号索引,需要注意控制边界。代码使用循环填表,由底向上填表实现。(也可以递归实现)代码使用LeetCode答题基本格式。
class Solution:
def longestValidParentheses(self, s: str) -> int:
if len(s) < 2:
return 0
dp = [0 for i in range(len(s))]
for i in range(len(s)):
if s[i] == "(":
pass
else:
if i-1 >= 0:
if s[i - 1] == "(":
# 此时和i号字符匹配的已经找到
dp[i] = dp[i - 2] + 2
else:
# 此时这个右括号可能是一个合法子串的结尾也可能不是
if s[i-1- dp[i - 1]] == "(" and i-1-dp[i-1] >=0:
dp[i] = 2 + dp[i - 1] + dp[i-1-dp[i-1]-1]
else:
pass
return max(dp)
补充说明
具体代码可以查看我的Github,欢迎Star或者Fork。此题为LeetCode动态规划的一题,题号为32,难度Hard。