注意这是个合法括号序列,所以我们可以看成是两颗有根树,且孩子是有先后关系的。删去一个 () 形状就等价于删去树的一个叶节点。另一个角度就是我们能否将 T2 “嵌入”到树 T1 中去。我们注意到这是一个子序列问题,所以对于 T2 的每个子树,肯定是在 T1 的子树中越靠前放进去越好。因此我们可以简单地写出一个 check 函数:
bool check(int u1, int u2) {
int j = 0;
for (int v : T2[u2]) {
while (j < T1[u1].size() && !check(T1[u1][j], v)) ++j;
if (j == T1[u1].size()) return false;
++j;
}
return true;
} 这个函数虽然是个 DFS,但是它其实是线性的,道理很简单,在寻找匹配的过程中,T1 的每个节点只会最多一次尝试与 T2 中的一个节点配对。因此总时间不超过 。综上,复杂度
。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <stack>
#include <random>
#include <bitset>
#include <queue>
#include <functional>
#include <set>
#include <map>
#include <vector>
#include <chrono>
#include <iostream>
#include <limits>
#include <numeric>
#define LOG(FMT...) fprintf(stderr, FMT)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
template <class T>
istream& operator>>(istream& is, vector<T>& v) {
for (T& x : v)
is >> x;
return is;
}
template <class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
if (!v.empty()) {
os << v.front();
for (int i = 1; i < v.size(); ++i)
os << ' ' << v[i];
}
return os;
}
const int N = 110;
int m1, m2;
vector<int> T1[N], T2[N];
void build(const string& s, int& m, vector<int> T[]) {
stack<int> stk;
stk.push(m = 1);
for (char c : s)
if (c == '(') {
T[stk.top()].push_back(++m);
stk.push(m);
} else
stk.pop();
}
bool check(int u1, int u2) {
int j = 0;
for (int v : T2[u2]) {
while (j < T1[u1].size() && !check(T1[u1][j], v)) ++j;
if (j == T1[u1].size()) return false;
++j;
}
return true;
}
int main() {
#ifdef LBT
freopen("test.in", "r", stdin);
int nol_cl = clock();
#endif
ios::sync_with_stdio(false);
cin.tie(nullptr);
string s;
cin >> s;
build(s, m1, T1);
cin >> s;
build(s, m2, T2);
cout << (check(1, 1) ? "Possible" : "Impossible");
#ifdef LBT
LOG("Time: %dms\n", int ((clock()
-nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
return 0;
} 
京公网安备 11010502036488号