对正串和反串分别建立 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;
}