牛客-回文串

题目传送门

sol:根据题意,可以把字符串截成两段,要求两段中最大的两个回文串相加最大的方案,于是跑马拉车维护一个前缀,再倒着跑马拉车维护后缀。因为最优方案肯定是在两个字母之间截,所以最后只考虑在'#'位置截断的情况

  • 马拉车
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int MAXN = 2e5 + 10;
    char s[MAXN * 2];
    int p[MAXN * 2];
    int pre[MAXN * 2], suf[MAXN * 2];
    int n, k;
    void manacher() {
        k = 0; memset(p, 0, sizeof(p));
        for (int i = 1; i < n; i++) {
            if (i < p[k] + k) {
                p[i] = min(k + p[k] - i, p[2 * k - i]);
            }
            while (s[i + p[i]] == s[i - p[i]]) {
                p[i] ++;
                pre[i + p[i] - 1] = max(pre[i + p[i] - 1], p[i] - 1);
            }
            pre[i] = max(pre[i], pre[i - 1]);
        }
        k = n; memset(p, 0, sizeof(p));
        for (int i = n - 1; i > 0; i--) {
            if (i > p[k] - k) {
                p[i] = max(0, min(i - k + p[k], p[2 * k - i]));
            }
            while (s[i + p[i]] == s[i - p[i]]) {
                p[i] ++;
                suf[i - p[i] + 1] = max(suf[i - p[i] + 1], p[i] - 1);
            }
            suf[i] = max(suf[i], suf[i + 1]);
        }
    }
    int main() {
        scanf("%s", s);
        n = strlen(s);
        for (int i = n; i >= 0; i--) {
            s[i + 1 << 1] = s[i];
            s[i << 1 | 1] = '#';
        }
        s[0] = '$';
        n = n + 1 << 1;
        manacher();
    /*
        puts(s);
        for (int i = 0; i < n; i++) printf("%x", p[i]);
        cout << endl;
        for (int i = 0; i < n; i++) printf("%x", pre[i]);
        cout << endl;
        for (int i = 0; i < n; i++) printf("%x", suf[i]);
        cout << endl;
    */
        int res = 0;
        for (int i = 3; i <= n - 3; i += 2) res = max(pre[i] + suf[i], res);
        printf("%d\n", res);
        return 0;
    }

猜你喜欢

转载自www.cnblogs.com/Angel-Demon/p/12182375.html
今日推荐