括号匹配(二)
时间限制:1000 ms | 内存限制:65535 KB
难度:6
描述
给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。
如:
[]是匹配的
([])[]是匹配的
((]是不匹配的
([)]是不匹配的
输入
第一行输入一个正整数N,表示测试数据组数(N<=10)
每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100
输出
对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行
样例输入
4
[]
([])[]
((]
([)]
样例输出
0
0
3
2
来源
《算法艺术与信息学竞赛》
上传者
张云聪
AC 0.32
一开始用栈来做,判断是否匹配,不匹配则计数+1,计算出不匹配个数。这种算法处理不了很多数据。
然后又改成两端向内依次寻找匹配,匹配则删去然后再寻找这个匹配里面的括号。一部分匹配完后,继续在没有匹配的数据中寻找,直到所有数据都被处理。这个算法比上面的好一点,但是不能处理()()或([)(])这样的特殊数据。
最后查到了用动态规划来做,然后又设计了第三种算法,成功AC。
动态规划思路:
把问题分为几个阶段,分别求解,将每一次运算的结果保存在一个二维数组中,以后的求解过程可以使用之前的运算结果。
本题思路:
可以把字符串分解,有以下这几种情况:
1.S="("等,此时dp=1.
2.S="(S')",S需要的最小括号数=S'
3.S="S'1+S'2",S需要的最小括号数=S'1的+S'2的
1.首先,建立一个二位数组dp[][]来存储结果,dp[i][j]表示从S[i]到S[j]的最少添加括号个数。
2.那么首先可以把dp[i][j](i==j)的值存为1,因为一个字符必定需要添加一个括号。其他值先赋值为0。(阶段0)
3.然后,进行循环遍历,每阶段考虑k个长度的区间(k从1到总长),每次考虑i,j内的区间(i从0开始依次考虑),如果S[i]==S[j],说明他们是匹配的,dp[i][j]=dp[i+1][j-1](括号里面部分需要添加的最小括号数)
4.此外,该题还有分割情况(比如"[[[]]][[[]]]"需要分成两部分考虑),应依次考虑所有分割情况,如果进行了某种分割后,最多需要添加括号更少,就采用更小的值,所以有for(int k = i;k<j;k++)dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);
5.全部处理完后,dp[0][length-1]即为S[0]S[length]也就是整个字符串需要添加的最小括号数。
又该题可以看出,动态规划实际遍历了所有可能结果,其中用二维数组保存一些结果,加快了运算速度。
手动演算"[((](]"的结果:
源码
#define R 105
#include<stdio.h>
#include<limits.h>
int length(char s[]){
int i=0;
while(s[i++]!='\0');
return i-1;
}
bool match(char a,char b){
if(a=='('&&b==')')return 1;
if(a=='['&&b==']')return 1;
return 0;
}
int min(int a,int b){
if(a<b)return a;
else return b;
}
int main(){
int n;
char all[R];
int dp[R][R];
int i,j,k,x;
int len;
scanf("%d",&n);
while(n--){
scanf("%s",all);
len = length(all);
for(i=0;i<len;i++)
for(j=0;j<R;j++)
dp[i][j]=0;
for(i=0;i<len;i++)
dp[i][i]=1;
for(k=1;k<=len;k++){//k为阶段数,表示处理长度为k的区间
for(i=0;i<len-k;i++){
j=i+k;
dp[i][j]=INT_MAX;
if(match(all[i],all[j]))
dp[i][j]=dp[i+1][j-1];
for(x=i;x<j;x++)
dp[i][j]=min(dp[i][j],dp[i][x]+dp[x+1][j]);
//printf("dp[%d][%d] = %d\n",i,j,dp[i][j]);
}
}
printf("%d\n",dp[0][len-1]);
}
return 0;
}
另外符上失败的两种算法(注意这两种是错的):
#include<stdio.h>
int length(char s[]){
int i=0;
while(s[i++]!='\0');
return i-1;
}
int main(){
int n,l,i,x,number;
char all[105],S[105];
scanf("%d",&n);
while(n--){
number = 0;
x = 0;
scanf("%s",all);
l = length(all);
for(i=0;i<l;i++){
if(all[i]=='('||all[i]=='['){
S[x] = all[i];
x++;
}
else if(all[i]==')'&&S[x-1]=='('){
x--;
}
else if(all[i]==']'&&S[x-1]=='['){
x--;
}
else{
number++;
}
}
number += x;
printf("%d\n",number);
}
return 0;
}
//该算法不足,放弃
#include<stdio.h>
int length(char s[]){
int i=0;
while(s[i++]!='\0');
return i-1;
}
char match(char a){
if(a == '(')
return ')';
else if(a == '[')
return ']';
else
return '\0';
}
int main(){
int n,l;
int a,number;
int i,j;
char all[105];
scanf("%d",&n);
while(n--){
number = 0;
scanf("%s",all);
l = length(all);
a = l - 1;
for(i=0;i<l;i++){
//printf("i==%d",i);
if(all[i]==')'||all[i]==']'){
number++;
}
else if(all[i]=='('||all[i]=='['){
for(j=a;j>=i;j--){
if(all[j]==match(all[i])){
//printf("match %d == %d\n",i,j);
all[j]='\0';
a=j-1;
break;
}
else if(i==j){
number++;
break;
}
}
}
else if(i>=a){
while(all[++i]=='\0');
i--;
a = i;
while(all[++a]!='\0'&&a<l);
a--;
}
}
printf("%d\n",number);
}
return 0;
}
问题地址传送门
资料传送门