P1944 最长括号匹配[线性dp或栈操作]

P1944 最长括号匹配

题意

最长括号匹配

题目描述

对一个由(,),[,]括号组成的字符串,求出其中最长的括号匹配子串。具体来说,满足如下条件的字符串成为括号匹配的字符串:

1.(),[]是括号匹配的字符串。

2.若A是括号匹配的串,则(A),[A]是括号匹配的字符串。

3.若A,B是括号匹配的字符串,则AB也是括号匹配的字符串。

例如:(),[],([]),()()都是括号匹配的字符串,而][,[(])则不是。

字符串A的子串是指由A中连续若干个字符组成的字符串。

例如,A,B,C,ABC,CAB,ABCABCd都是ABCABC的子串。空串是任何字符串的子串。

输入格式

输入一行,为一个仅由()[]组成的非空字符串。

输出格式

输出也仅有一行,为最长的括号匹配子串。若有相同长度的子串,输出位置靠前的子串。

样例 #1
样例输入 #1
([(][()]]()
样例输出 #1
[()]
样例 #2
样例输入 #2
())[]
样例输出 #2
()
提示

【数据范围】

对20%的数据,字符串长度<=100.

对50%的数据,字符串长度<=10000.

对100%的数据,字符串长度<=1000000.


tags

线性dp

思路

解法1

  1. 类最长上升子序列
  2. dp[i]表示:以第i个元素结尾的最长括号匹配
  3. 状态转移:
    1. 若s[i]为[或(则dp[i]=0
    2. dp[i]可以从dp[i-1]转移过来,即从以s[i-1]为结尾的最长括号匹配。若s[i-1-dp[i-1]]==s[i],则dp[i]=dp[i-1]+2
    3. 若不满足,那么s[i]就不能加到前面去了,则dp[i]=0
    4. 但是注意题目中第三个条件:“若A,B是括号匹配的字符串,则AB也是括号匹配的字符串。”,我们还需去加上以s[i-2-d[i-1]]为结尾的最长括号匹配

AC代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int maxn=1e6+10;
int dp[maxn];

int main(){
    string s;
    cin>>s;
    int len=s.length();
    s='*'+s;
    int ans=0,l=0,r=0;
    for(int i=2;i<=len;i++){
        if(s[i]=='['||s[i]=='(')continue;
        if((s[i]==']'&&s[i-1-dp[i-1]]=='[')||(s[i]==')'&&s[i-1-dp[i-1]]=='(')){
            dp[i]=dp[i-1]+2+dp[i-2-dp[i-1]];
            if(ans<dp[i]){
                ans=dp[i];
                l=i-dp[i]+1;
                r=i;
            }
        }
    }
    for(int i=l;i<=r;i++)cout<<s[i];
    cout<<endl;
    return 0;
}

解法2

  1. 括号匹配:如果一个括号串中’(‘与’)'能相互抵消(要临近!),那么这两括号相互匹配
    1. ())中只有s[0]与s[1]能够相互抵消
    2. (()())(中s[1]与s[2]抵消,s[3]与s[4]抵消,当他们抵消完之后s[0]与s[5]可以相互抵消
  2. 我们就可以设置一个,将元素逐个入栈,若出现栈顶元素与s[i]想匹配,则出栈(以实现抵消操作让其他匹配的括号临近)
  3. 我们所求的最长括号匹配就是在出栈的时候被更新
  4. 注意如果出栈后栈为空,那么说明从0到i都是括号匹配,要单独考虑

AC代码

#include<bits/stdc++.h>
using namespace std;

int main(){
    string s;
    cin>>s;
    stack<int>st;
    int ans=0,l,r;
    for(int i=0;i<s.length();i++){
        if(s[i]=='('||s[i]=='[')st.push(i);//如果为(或[肯定直接入栈
        else if(st.empty()||s[st.top()]==')'||s[st.top()]==']'||(s[st.top()]=='('&&s[i]==']')||(s[st.top()]=='['&&s[i]==')'))st.push(i);//如果栈为空或者没有括号匹配,则入栈
        else{//如果非空且有括号匹配,则更新状态
            st.pop();//先出栈
            if(st.empty()){//栈空时,此时有括号匹配从0到i,看是否可以去更新
                if(ans<i+1)ans=i+1,l=0,r=i;
            }
            else {
                if(ans<i-st.top())ans=i-st.top(),l=st.top()+1,r=i;//栈非空时,有括号匹配从st.top()+1到i,看是否可以更新
            }
        }
    }
    for(int i=l;i<=r;i++)cout<<s[i];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值