落谷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;
}
原文地址:https://www.cnblogs.com/zcr-blog/p/12394727.html