P4287 [SHOI2011]双倍回文(回文树建trans)

题目:https://www.luogu.org/problem/P4287
求最长双倍回文。

容易发现双倍回文的前半部分一定与整个回文在同一条fail链上,因此可以想出一种跳fail链统计答案的解法。

对于每个点,往上跳fail,直到跳到一个点,这个点的长度*2刚好是起始点的两倍,再判断一下起始点长度能否被4整除,都满足即可统计答案。

该解法如同各种自动机的连续跳fail做法一样,在fail树是一条链的时候复杂度退化到n方,需要用各种手法来优化。

首先可以想到用倍增来优化,这样复杂度可以变成O(nlogn),应该就可以通过这题了。

但是我在洛谷讨论区看到了一种非常有意思的解法,建一个“trans边”,即受你想要的要求限制的fail,在建树时顺便求出。

这是回文树找fail的代码:

    int getFail(int x) {
        while (S[n - len[x] - 1] != S[n]) {
            x = fail[x];
        }
        return x;
    }

那么这题要的trans只需要加一点点限制:

    int getTrans(int x, int now) {
        while (S[n - len[x] - 1] != S[n] || (len[x] + 2) * 2 > len[now]) {
            x = fail[x];
        }
        return x;
    }

就可以在O(n)的时间内愉快的找到想要的点。

这种思想非常有意思,这样可以针对不同的要求,连出不同的fail边。

ac代码:

#include <bits/stdc++.h>

using namespace std;

const int maxn = 5e5 + 5;

int n;
char s[maxn];

struct Pam {
    int next[maxn][26];
    int fail[maxn], trans[maxn];
    int len[maxn];
    int S[maxn];
    int last, n, p;

    int newNode(int l) {
        memset(next[p], 0, sizeof(next[p]));
        len[p] = l;
        return p++;
    }

    void init() {
        n = last = p = 0;
        newNode(0);
        newNode(-1);
        S[n] = -1;
        fail[0] = 1;
    }

    int getFail(int x) {
        while (S[n - len[x] - 1] != S[n]) {
            x = fail[x];
        }
        return x;
    }

    int getTrans(int x, int now) {
        while (S[n - len[x] - 1] != S[n] || (len[x] + 2) * 2 > len[now]) {
            x = fail[x];
        }
        return x;
    }

    void add(int c) {
        S[++n] = c;
        int cur = getFail(last);
        if (!next[cur][c]) {
            int now = newNode(len[cur] + 2);
            fail[now] = next[getFail(fail[cur])][c];
            next[cur][c] = now;
            if (len[now] <= 2) {
                trans[now] = fail[now];
            } else {
                trans[now] = next[getTrans(trans[cur], now)][c];
            }
        }
        last = next[cur][c];
    }

    void build() {
        init();
        for (int i = 0; s[i]; i++) {
            add(s[i] - 'a');
        }
    }

    void solve() {
        int ans = INT_MIN;
        for (int i = 2; i < p; ++i) {
            if (len[trans[i]] * 2 == len[i] && len[i] % 4 == 0) {
                ans = max(ans, len[i]);
            }
        }
        printf("%d\n", ans);
    }

} pam;

int main() {
    scanf("%d%s", &n, s);
    pam.build();
    pam.solve();
    return 0;
}
发布了156 篇原创文章 · 获赞 20 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/Cymbals/article/details/102169930