zoukankan      html  css  js  c++  java
  • [Luogu1944] 最长括号匹配

    Description

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

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

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

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

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

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

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

    Input

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

    Output

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

    Sample Input1

    ([(][()]]()
    

    Sample Output1

    [()]
    

    Sample Input2

    ())[]
    

    Sample Output2

    ()
    

    Hint

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

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

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

    题解

    对于这题(le 1000000)的数据规模显然只允许我们用一重循环

    最长,可见这是道最值问题

    最值问题可以用贪心(DP)二分(etc)

    我对于这题用的是(DP)


    进入正题:如何(DP)

    首先,我们需要构建状态,状态的构建不是唯一的,我是这样构建的

    (f[i])表示(s[i])为结尾的字符串的最长括号匹配

    接下来,我们就得推状态转移方程

    考虑(s[i]),要想它能构成括号匹配,很显然地,它肯定不能为(或者[

    那么(s[i]))或者]时我们应该怎样转移呢?

    我们要找到一个(s[k]=)((或者[,为了方便叙述,下同),使得在((k,i))这个开区间内的字符串为最长括号匹配([k,i])这个闭区间尽可能得大

    那么什么情况能满足上面的条件呢?

    (f[i-1])表示什么?不正是以(s[i-1])结尾的最长括号匹配吗,那么如果(s[i-1-f[i-1]])(s[i])匹配的话,(f[i])一定等于(f[i-1]+2+f[i-f[i-1]-2])

    说明一下

    • (f[i-1])代表(s[i-1-f[i-1]])(s[i])中间的一段即(s[i-1])的最长括号匹配
    • (2)(s[i-1-f[i-1]])(s[i])匹配,增加长度为(2)
    • (s[i-f[i-1]-2])(s[i-f[i-1]-1])的前一个字符,这里不要漏掉它还可以构成的最长括号匹配的长度
    • 不是很复杂,画个图就很明显了

    那么若(s[i-1-f[i-1]])(s[i])不匹配怎么办?这时(f[i])肯定为(0),因为除此之外,不管选哪个字符,都没法满足它和(s[i])构成括号匹配

    (my~Code)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    const int L=1000005;
    char s[L];
    int l,f[L],Ans,id;
    
    int main()
    {
    	scanf("%s",s+1);
    	l=strlen(s+1);
    	for(int i=2;i<=l;++i)
    		if(s[i]=='('||s[i]=='[') continue;
    		else
    			if((s[i]==')'&&s[i-f[i-1]-1]=='(')
    			||(s[i]==']'&&s[i-f[i-1]-1]=='['))
    			{
    				f[i]=f[i-1]+2+f[i-f[i-1]-2];
    				if(f[i]>Ans) Ans=f[i],id=i;
    			}
    	for(int i=id-Ans+1;i<=id;++i) printf("%c",s[i]);
    	putchar('
    ');
    	return 0;
    }
    
  • 相关阅读:
    linux下查看当前登陆的用户数目
    uboot能ping通本机无法ping通本机上搭建的虚拟机
    一个时序图描述从用户在浏览器地址栏输入url并按回车,到浏览器显示相关内容的各个过程
    3*0.1 == 0.3 将会返回什么?true 还是 false?
    Floating Point Math
    浮点数在计算机中是如何表示的
    浮点数在计算机中是如何表示的
    Java并发编程:volatile关键字解析
    join的源码
    i++ 是线程安全的吗
  • 原文地址:https://www.cnblogs.com/hihocoder/p/12416385.html
Copyright © 2011-2022 走看看