题目链接:http://acm.hunnu.edu.cn/online/?action=problem&type=show&id=11757
题意: 给偶数长的由‘(’、‘)’组成的字符串s,问是否为合法括号序列或进行一段
连续序列翻转(‘(’->‘)’、‘)’->‘(’)是否为合法序列。(len(s)<=5000)
·这是博主大一暑假省赛的热身赛题,太菜了,到今天才补成功!当时,开了4个
F[5000][5000]的数组(记录字符串的任意一段区间的匹配后所剩‘)’、‘(’的个数,
预先处理好所有子区间翻转、不翻转匹配后不合法的‘)’、‘(’的个数,先判断不
翻转时是否是个合法的括号序列,不然则枚举所有翻转区间段,将原串分成三段,
1、前段的不合法的‘)’必须为0;
2、后段的不合法的‘(’必须为0,同上;
3、前段的‘(’能够匹配完翻转段的‘)’,前段的不合法的‘(’+翻转段的‘(’==翻转段的‘)’+后段的不合法的‘)’,即此时翻转产生了合法的序列!
好是好,但是开了1亿的数组,爆了内存!
其实我们根本就不用开二维数组来保存所有的子段,从上面的图中我们可以分析出在不翻转的子段中仅用到了前缀子串、后缀子串,所以我们仅用O(2*n)*2来存储匹配完成后的前缀、后缀的不合法左右括号数目;而翻转子段,因为我们需要枚举翻转子区段的左端点,而右端点是连续递增的,每次都向右拓展1个单元,所以根本就不用预处理储存!(滚动更新的!)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <math.h>
#define Maxn 5010
using namespace std;
int L[2][Maxn]={0},R[2][Maxn]={0};
char s[Maxn];
int inverR,inverL;
int main(){
scanf("%s",s);
int str=strlen(s);
for(int i=1;i<=str;++i){
L[0][i]=L[0][i-1];
R[0][i]=R[0][i-1];
if(s[i-1]==')'){
if(L[0][i]>0) L[0][i]--;
else R[0][i]++;
}else L[0][i]++;
}
for(int i=str;i>=1;--i){
L[1][i]=L[1][i+1];
R[1][i]=R[1][i+1];
if(s[i-1]=='('){
if(R[1][i]>0) R[1][i]--;
else L[1][i]++;
}else R[1][i]++;
}
int flag=1;
if(!L[0][str]&&!R[0][str]) {printf("possible\n");return 0;}
//原本不翻转就是合法序列
for(int i=1;i<=str&&flag;++i){
if(R[0][i-1]) continue;//有前缀')'
inverL=inverR=0;
for(int j=i;j<=str&&flag;++j){
if(s[j-1]=='('){
if(inverL>0)--inverL;
else inverR++;
}else inverL++;
if(L[1][j+1]) continue;//有后缀'('
if(L[0][i-1]>=inverR&&L[0][i-1]+inverL==R[1][j+1]+inverR)
flag=0;
}
}
if(flag)printf("impossible\n");
else printf("possible\n");
return 0;
}