Codeforces 2144F Bracket Groups 题解 [ 紫 ] [ AC 自动机 ] [ DP ] [ 构造 ]
Bracket Groups:赛时猜出来用 ACAM,结果没猜到结论,我是糖比。
首先判掉一些 corner,如果出现了 \(\texttt{()}\) 为单个字符串,则一定无解。
发现后面不太好做,所以可以套路地猜一猜答案上界,发现最多只需要分成两组。具体地,考虑往极端情况构造,弄出下面两种括号串:
- \(\texttt{((((((} \cdots\texttt{))))))}\)。
- \(\texttt{()()()} \cdots\texttt{()()()}\)。
考虑证明,如果 \(s\) 不是这两个串中任意一个串的子串,则放哪一组都可以。如果 \(s\) 为第一个串的子串,那么 \(s\) 中一定含有两个相同且相邻的字符(一种 corner 是 \(\texttt{)(}\) 的情况,此时也会放到第一组),而这是不可能出现在第二个串中的。如果 \(s\) 为第一个串的子串则也是同理的。
一种判断是否为第一个串的子串的 trick:括号串转 \(1, -1\) 的前缀和,然后判断前缀和之极差是否小于 \(2\)。
剩下的只需要考虑能否被一个括号串全部包含即可。这是个经典的 ACAM 上 DP 的问题。\(dp_{i, j, k}\) 表示考虑到第 \(i\) 个字符,括号串前缀和为 \(j\),ACAM 上在节点 \(k\) 处是否可行,然后转移的时候记录一下前驱即可。最后反过来搜一遍构造出方案。
时间复杂度 \(O(nk^3)\)。
#include <bits/stdc++.h>
#define fi first
#define se second
#define eb(x) emplace_back(x)
#define pb(x) push_back(x)
#define lc(x) (tr[x].ls)
#define rc(x) (tr[x].rs)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
using pi = pair<int, int>;
const int N = 55, V = 3005;
int n, m, ch[V][2], idx, tag[V], ne[V];
int s[N][N], slen[N];
char ts[N];
void insert(int id)
{
int p = 0;
for(int i = 1; i <= slen[id]; i++)
{
if(ch[p][s[id][i]] == 0) ch[p][s[id][i]] = ++idx;
p = ch[p][s[id][i]];
}
tag[p] = 1;
}
vector<int> g[V];
void dfs(int u)
{
for(auto v : g[u])
{
tag[v] |= tag[u];
dfs(v);
}
}
void build()
{
queue<int> q;
for(int i = 0; i < 2; i++)
{
if(ch[0][i]) q.push(ch[0][i]);
}
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = 0; i < 2; i++)
{
int v = ch[u][i];
if(v) { ne[v] = ch[ne[u]][i]; q.push(v); }
else ch[u][i] = ch[ne[u]][i];
}
}
for(int i = 1; i <= idx; i++) g[ne[i]].push_back(i);
dfs(0);
}
bitset<V> dp[N][N];
tuple<int, int, int> pre[N][N][V];
char ans[N];
vector<int> ans2, ans3;
void construct(int p)
{
int i = m, j = 0;
while(i)
{
tuple<int, int, int> tmp = pre[i][j][p];
ans[i] = (get<1>(tmp) - j > 0 ? ')' : '(');
j = get<1>(tmp);
p = get<2>(tmp);
i--;
}
cout << 1 << "\n";
cout << ans + 1 << "\n";
cout << n << "\n";
for(int i = 1; i <= n; i++) cout << i << " ";
}
int main()
{
//freopen("sample.in", "r", stdin);
//freopen("sample.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> ts + 1;
slen[i] = strlen(ts + 1);
if(slen[i] == 2 && ts[1] == '(' && ts[2] == ')')
{
cout << "-1";
return 0;
}
for(int j = 1; j <= slen[i]; j++)
s[i][j] = (ts[j] == '(' ? 1 : 0);
insert(i);
}
build();
dp[0][0][0] = 1;
for(int i = 0; i < m; i++)
for(int j = 0; j <= m; j++)
for(int k = 0; k <= idx; k++)
{
if(dp[i][j][k] == 0) continue;
if(tag[ch[k][0]] == 0 && j - 1 >= 0)
{
dp[i + 1][j - 1][ch[k][0]] = 1;
pre[i + 1][j - 1][ch[k][0]] = {i, j, k};
}
if(tag[ch[k][1]] == 0)
{
dp[i + 1][j + 1][ch[k][1]] = 1;
pre[i + 1][j + 1][ch[k][1]] = {i, j, k};
}
}
for(int i = 0; i <= idx; i++)
{
if(dp[m][0][i])
{
construct(i);
return 0;
}
}
cout << "2\n";
for(int i = 1; i <= m; i++)
{
if(i & 1) cout << "(";
else cout << ")";
}
cout << "\n";
for(int i = 1; i <= n; i++)
{
int mn = 0, mx = 0, now = 0;
for(int j = 1; j <= slen[i]; j++)
{
if(s[i][j] == 0) now--;
else now++;
mn = min(mn, now);
mx = max(mx, now);
}
if(mx - mn >= 2) ans2.push_back(i);
else ans3.push_back(i);
}
cout << ans2.size() << "\n";
for(auto itm : ans2) cout << itm << " ";
cout << "\n";
for(int i = 1; i <= m / 2; i++)
cout << "(";
for(int i = 1; i <= m / 2; i++)
cout << ")";
cout << "\n";
cout << ans3.size() << "\n";
for(auto itm : ans3) cout << itm << " ";
cout << "\n";
return 0;
}

浙公网安备 33010602011771号