回文串[APIO2014](回文树)

题目描述

给你一个由小写拉丁字母组成的字符串 s。我们定义 s 的一个子串的存在值为这个子串在 s中出现的次数乘以这个子串的长度。对于给你的这个字符串 s,求所有回文子串中的最大存在值。

输入格式

一行,一个由小写拉丁字母(a~z)组成的非空字符串 s。

输出格式

输出一个整数,表示所有回文子串中的最大存在值。

样例

输入样例 1

abacaba

输出样例 1

7

输入样例 2

www

输出样例 2

4

首先建出s的回文树,然后对于每一个回文子串,记录cnt为它出现的次数。

对于它fail树上的儿子,肯定都是它的子串(后缀),所以他出现了cnt次,它的后缀也会出现cnt次。

我们从更新的节点从后往前遍历,假设现在遍历到i,就使cnt[fail[i]]+=cnt[i]。然后更新ans=max(ans, cnt[i]*len[i])。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 400000;
char s[N];
int lens;
long long ans;
namespace Plalindromic_Tree{
    struct node{
        int go[26];
        int fail, len;
    }pt[N];
    int lst = 0, tot = 0;
    int cnt[N];
    void build() {
        s[0] = -1;
        pt[++tot].len = -1;
        pt[0].fail = pt[1].fail = 1;
    }
    void add(int c, int n) {
        int p = lst;
        while (s[n - pt[p].len - 1] != s[n]) p = pt[p].fail;
        if (!pt[p].go[c]) {
            int v = ++tot, k = pt[p].fail;
            pt[v].len = pt[p].len + 2;
            while (s[n - pt[k].len - 1] != s[n]) k = pt[k].fail;
            pt[v].fail = pt[k].go[c];
            pt[p].go[c] = v;
        }
        lst = pt[p].go[c];
        cnt[pt[p].go[c]]++;
    }
}using namespace Plalindromic_Tree;
int main() {
    scanf("%s", s + 1);
    lens = strlen(s + 1);
    build();
    for (int i = 1; i <= lens; i++) {
        add(s[i] - 'a', i);
    }
    for (int i = tot; i; i--) {
        cnt[pt[i].fail] += cnt[i];
        ans = max(ans, 1ll * cnt[i] * pt[i].len);
    }
    cout << ans;
    return 0;
}
 
原文地址:https://www.cnblogs.com/zcr-blog/p/12298869.html