2022CCPC Online Contest G - Name the Puppy
对正串和反串分别建立 Trie 树,定义 \(dp[i][j]\) 表示正串 Trie 树上编号为 \(i\) 的点匹配反串 Trie 树上编号为 \(j\) 的点所能拼出最长 anti-border 的长度。
如此,从根节点开始搜索,直到无法匹配为止都可以搜,搜到底后回到根节点继续匹配,可以证明,拼出来的 anti-border 不是小于 \(|\Sigma|\) 的一半,就是 INF
,考虑 INF
的情况,必定会出现恰好匹配完两个串,我们跳回根节点,如果两个指针都在根节点,那么说明可以无限加长使得答案为 INF
,反之是有穷的。
时间复杂度 \(O(n^2)\),带一个小常数,可以通过。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010;
int n, ans;
int dp[N][N], tr[2][N][26], idx[2];
int st[2][N * 52];
void insert(string str, int op) {
int p = 0;
for (int i = 0; str[i]; i ++ ) {
int u = str[i] - 'a';
if (!tr[op][p][u]) tr[op][p][u] = ++ idx[op];
p = tr[op][p][u];
}
st[op][p] = 1;
}
void dfs(int x, int y) {
if (st[0][x] && st[1][y]) return ans = -1, void();
if (ans == -1) return;
ans = max(ans, dp[x][y]);
for (int i = 0; i < 26; i ++ ) {
int nx = tr[0][x][i], ny = tr[1][y][i];
int rx = tr[0][0][i], ry = tr[1][0][i];
if (nx && ny) {
dp[nx][ny] = dp[x][y] + 1;
dfs(nx, ny);
} else if (st[0][x] && rx && ny) {
dp[rx][ny] = dp[x][y] + 1;
dfs(rx, ny);
} else if (st[1][y] && nx && ry) {
dp[nx][ry] = dp[x][y] + 1;
dfs(nx, ry);
} else if (st[0][x] && st[1][y] && rx && ry) {
dp[rx][ry] = dp[x][y] + 1;
dfs(rx, ry);
}
}
}
void solve() {
cin >> n;
for (int i = 0; i < n; i ++ ) {
string s;
cin >> s;
insert(s, 0);
reverse(s.begin(), s.end());
insert(s, 1);
}
memset(dp, -1, sizeof dp);
dp[0][0] = 0;
dfs(0, 0);
if (~ans) cout << ans << "\n";
else cout << "INF\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}