[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('\n');
	return 0;
}
posted @ 2020-03-04 21:42  OItby  阅读(823)  评论(0编辑  收藏  举报