题目描述
给你一个合法的括号序列s1,每次你可以删除一个"()"
你可以删除0个或者多个"()"
求能否删成另一个括号序列s2
输入描述:
第一行输入一个字符串s (2 ≤ |s| ≤ 100) 第二行输入一个字符串t (2 ≤ |t| ≤ 100 )
输出描述:
如果可以输出"Possible" 否则输出"Impossible"
示例1
输入
(()) ()
输出
Possible
示例2
输入
()
()
输出
Possible
示例3
输入
(()()())
((()))
输出
Impossible
示例4
输入
((())((())())())
(()(())())
输出
Possible
示例5
输入
((())((())())()) ((()()()()()))
输出
Impossible
备注:
子任务1: |s| <= 10 子任务2: |s| <= 20 子任务3: 无限制
解题思路
看完题目我首先想的是暴力模拟,判断每一对“()”是否删除,但是这样写的话需要对每一对括号进行标记,时间复杂度会有2^50,超时所以不行。
既然如此那就换一种思路,我们可以用一个数组dp[i][j][k]来表示字符串s前i位与字符串t前j位的匹配情况(k=删除的 '(' 数-删除的')'数,即多删除的'('数),如果dp[s.len][t.len][0]=true,则表示s可以变成t,反之则不行,那我们就从dp[0][0][0]=1开始往后判断。
我们可以这么思考,如果s可以变成t,s’(由s去掉末尾的一小部分而来)可以变成t或t'(t'和s'类似),s''同理,则状态转移方程为
dp[ii][jj][kk]=(dp[i][j][k])&true;(ii,jj,kk为i,j,k变化而来,可能相同,可能加1)
即最终的dp[s.len][t.len][0]是由dp[i][j][k]变化而来,dp[i][j][k]同理
则我们需要找到dp[i][j][k]为true
当状态dp[i][j][k]=true的情况下,对于两个串的下一个字符,只有以下四种情况:
我们分情况讨论:第一种情况,可以推出dp[i+1][j+1][k]=true或dp[i+1][j][k+1]=true;
对于第二种情况可以推出dp[i+1][j][k+1]=true;
对于第三种情况可以推出dp[i+1][j+1][k]=true或dp[i+1][j][k-1]=true;
对于第四种情况可以推出dp[i+1][j][k-1]=true;
对表达式进行归类,相同的放在一起,总结得出如果后一个字符相等且k=0,那么dp[i+1][j+1][0]=true,如果s的后一个字符是左括号,那么dp[i+1][j][k+1]=true,如果都不满足,并且k!=0,那么dp[i+1][j][k-1]=true。
思路都清晰了,那我们直接按照思路写代码就可以了。
题解代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[110],t[110];
bool dp[110][110][60];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
///关闭输入输出流
cin>>s+1>>t+1;
int p=strlen(s+1);
int q=strlen(t+1);
dp[0][0][0]=1;
for(int i=0;i<=p;i++){
for(int j=0;j<=q;j++){
for(int k=0;k<=p/2;k++){
///状态转移方程dp[ii][jj][kk]=(dp[i][j][k])&true;
if(dp[i][j][k]){///找到dp[i][j][k]为true
if(!k&&s[i+1]==t[j+1])dp[i+1][j+1][k]=1;
if(s[i+1]=='(')dp[i+1][j][k+1]=1;
else if(k)dp[i+1][j][k-1]=1;
///分情况讨论的总结,从i,j,k变为ii,jj,kk,分情况而定
}
}
}
}
if(dp[p][q][0])cout<<"Possible\n";
else cout<<"Impossible\n";
return 0;
}