落谷P1872 回文串计数(回文树)

Posted zcr-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了落谷P1872 回文串计数(回文树)相关的知识,希望对你有一定的参考价值。

传送门

这道题显然可以用PAM做出来。

PAM可以算出以字符串的第ii个字符为结尾的回文子串的个数。我们将其存到一个数组l[n],再求一个前缀和就可以把字符串的前i个字符的前缀有多少个回文子串求出来。

然后,我们将PAM清空,倒着做一遍,就可以求出以第i个字符为左端点的回文子串个数r[i]。与它不相交的回文子串且在它前面的子串有l[i - 1]个,相乘再累加就是答案。

此题在落谷的评级是绿,那是因为此题数据范围只有2000,不用PAM也可以做。但此题可以当做PAM入门的练手题。

#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int N = 200000;
struct Plalindromic_Tree{
    int go[26], len, fail;
    ll fail_len;
}pt[N];
int lst, tot;
char s[N], ss[N];
void build() {
    lst = tot = 0;
    ss[0] = -1;
    pt[++tot].len = -1;
    pt[0].fail = pt[1].fail = 1;
    pt[0].fail_len = 2;
    pt[1].fail_len = 1;
}
int add(int c, int n) {
    int p = lst;
    ss[n] = c + a;
    while (ss[n - pt[p].len - 1] != ss[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 (ss[n - pt[k].len - 1] != ss[n]) k = pt[k].fail;
        pt[v].fail = pt[k].go[c];
        pt[v].fail_len = pt[pt[v].fail].fail_len + 1;
        pt[p].go[c] = v;
    }
    return lst = pt[p].go[c];
}
ll r[N], ans;
int lens;
int main() {
    scanf("%s", s + 1);
    lens = strlen(s + 1);
    build();
    for (int i = 1; i <= lens; i++) {
        r[i] = (pt[add(s[i] - a, i)].fail_len - 2) + r[i - 1];
    }
//    cout<< endl;
    memset(pt, 0, sizeof(pt));
    build();
    for (int i = lens; i > 1; i--) {
        ans += (pt[add(s[i] - a, lens - i + 1)].fail_len - 2) * r[i - 1];
    }
    cout << ans;
    return 0;
}

以上是关于落谷P1872 回文串计数(回文树)的主要内容,如果未能解决你的问题,请参考以下文章

回文树或者回文自动机,及相关例题

回文树模板

回文树总结

Palindromic Tree 回文自动机-回文树 例题+讲解

bzoj3676: [Apio2014]回文串 回文树

[算法模版]回文树