2022CCPC Online Contest G - Name the Puppy

image

对正串和反串分别建立 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;
}
posted @ 2025-03-27 23:42  YipChip  阅读(19)  评论(0)    收藏  举报