回文树入门

回文串  HYSBZ - 3676

考虑一个只包含小写拉丁字母的字符串s。我们定义s的一个子串t的“出 
现值”为t在s中的出现次数乘以t的长度。请你求出s的所有回文子串中的最 
大出现值。 

建一颗回文树,然后getans统计答案就OK了.  (回文树见IOI2017论文)

#include <cstdio>

const int maxn = 300050;

class pal {
public:
    int next[maxn][26];
    int fail[maxn];
    int len[maxn];
    int cnt[maxn];
    char str[maxn];
    int last;
    int tot;
    int n;
    
    // 记录节点长度,同时返回一个节点 
    inline int node(int _len) {
        len[tot] = _len;
        return tot++;
    }
    
    // odd根初始化为: 长度为-1  even根初始化len为0
    // 同时str[0]设置为字符集没有的字符 用于判断.
    // even根的fail指针是odd根 odd根的fail指针是自身. 
    void init() {
        last = tot = n = 0;
        node(0); node(-1);
        str[0] = -1;  fail[0] = fail[1] = 1; 
    }
    
    // 获得添加字符后,可以组成回文串的位置. 
    inline int GetFail(int cur) {
        while (str[n-len[cur]-1] != str[n])
            cur = fail[cur];
        return cur;
    }
    
    // 添加新字符  
    void insert(char ch) {
        str[++n] = ch;  
        int cur = GetFail(last), now;
        if (!next[cur][ch]) {
            now = node(len[cur] + 2);
            fail[now] = next[GetFail(fail[cur])][ch]; //和下一句的顺序不能反 
            next[cur][ch] = now;
        }
        ++cnt[last = next[cur][ch]]; // 出现次数,但是不完全统计,还需要for来统计.(因为一个长的回文串可能会包含一些小的回文串.) 
    }
    
    long long getans() {
        int i;
        long long res = 0;
        for (i=tot-1; i; --i)      //  统计出现次数 
            cnt[fail[i]] += cnt[i];
        for (i=tot-1; i; --i) 
            if (res < 1ll*len[i]*cnt[i])
                res = 1ll*len[i]*cnt[i];
        return res;        
    }
}pal;


char s[300050];

int main() {
//    freopen("E:\input.txt", "r", stdin);
    pal.init();
    scanf("%s", s);
    for (char *p=s; *p; p++) 
        pal.insert(*p - 'a');
    printf("%lld
", pal.getans());
    return 0;
}
原文地址:https://www.cnblogs.com/cgjh/p/9597551.html