回文自动机 PAM

回文自动机 PAM

https://oi-wiki.org/string/pam/#_4

临时口胡

因为来不及做了,先组一套题目,感受一下题型。

回文自动机(2018ACM-ICPC南京赛区网络赛: I. Skr)

长度为n的数字串,只包含数字1~9,求所有本质不同的回文串代表的整数之和

例如:“1232111”:ans = 1+11+111+2+3+232+12321 = 12681

可以预处前缀和,然后记录每个回文串的位置。


2019杭电多校二 I Love Palindrome String(回文自动机)

给出一个长度为N的字符串,要求输出一个长度为N的统计数组A

A[i]表示长度为i的good substring的数量

good substring 的定义是 该子串是回文串,且该子串的一半也是回文串。

预处理字字符串哈希,遍历所有回文子串云云。


2014 Asia Xian Regional Contest G

给出两个字符串A,B。求A的所有回文串在B中出现次数的和。

可能是对两个树搜索公共部分···


2019牛客暑期多校训练营(第六场)C:Palindrome Mouse(回文树+树剖)

https://blog.csdn.net/cymbals/article/details/98398375

给定字符串Str,求出回文串集合为S,问S中的(a,b)满足a是b的子串的对数。

https://ac.nowcoder.com/acm/contest/886/C

我感觉,或许和链深度有关。


luogu 最长双回文串

输入长度为nnn的串SSS,求SSS的最长双回文子串TTT,即可将TTT分为两部分XXX,YYY,(∣X∣,∣Y∣≥1|X|,|Y|≥1∣X∣,∣Y∣≥1)且XXX和YYY都是回文串。

我觉得是排除掉直接fail连向根的节点去找。


拉拉队排练

大致意思就是让你求出一个字符串中所有的奇回文串,并把它们的长度前k的长度连乘

由于答案可能很大,输出除以19930726的余数。

马拉车也可做,但是回文树奇根和偶根是独立的,也可以操作的样子。

(但我有奇偶分离的马拉车,其实还好。


「SHOI2011」双倍回文

记t为s翻转后的串,求一个串最长的形如stst的子串长度。

= = 原来这就是19年杭电多校的原题啊。打扰了。


HDU 5421 Victor and String

BestCoder Round #52 (div.1)

给你n次操作,

如果为1,则在字符串后面插入一个字符,

如果为2,则在字符串前面插入一个字符,

如果为3,则输出当前的字符串中的本质不同的回文串的个数,

如果为4,则输出字符串的回文串的个数。

看起来要双向维护pam,这个变形比较需要基本原理,有点难度。

CodeChef Palindromeness


最近的gym 也有一道题。 H. happy game

思考

struct 还是 namespace 这是个问题。

两者各有各的好处。

namespace 的优势在于,如果你的逻辑都在一个模板里改,那么会很方便,但是在外面需要敲范围解析运算符::。而且在某些情况下,会避免初始化的问题。

struct 的优势在于,要根外部逻辑结合的时候,就一个点符号是比较方便的。而且容易让人去意识到作用域的问题。

我觉得两种方法都有可取的地方。

现场赛的时候甚至可能要直接敲成全局(如果是裸题的话)

2019 ICPC 徐州站 网络赛 G

https://nanti.jisuanke.com/t/41389

题意

设一个回文串的贡献为其包含的不同字符的个数,则给出一个原串,求其所有回文子串的贡献之和。

思路

知道PAM可以遍历所有的回文子串,那么只要得知每个回文子串的位置即可。

这个信息很好维护。

至于求权值可以借助前缀做。

代码

namespace版本

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int PAMN = 3e5 + 59;
namespace PAM {
    
    
    int tot, las;
    int ch[PAMN][26];
    int cnt[PAMN];
    int len[PAMN];
    int fail[PAMN];
    int npos[PAMN];

    int pos;
    char s[PAMN];

    int node(int l) {
    
    
        tot++;
        memset(ch[tot], 0, sizeof(ch[tot]));
        len[tot] = l;
        cnt[tot] = 0;
        fail[tot] = 0;
        npos[tot] = pos;	// 标记回文串第次出现的位置
        return tot;
    }

    void init() {
    
    
        tot = -1;
        las = 0;
        pos = 0;
        s[pos] = '$';   // s = '$......'
        node(0);      // node[0] : len =  0 偶根
        node(-1);     // node[1] : len = -1 奇根
        fail[0] = 1;
    }

    int jump(int u) {
    
    
        while (s[pos - len[u] - 1] != s[pos]) {
    
    
            u = fail[u];
        }
        return u;
    }

    void extend(char c) {
    
    
        s[++pos] = c;
        int d = c - 'a';
        int u = jump(las);
        if (!ch[u][d]) {
    
    
            int v = node(len[u] + 2);
            fail[v] = ch[jump(fail[u])][d];
            ch[u][d] = v;
        }
        npos[ch[u][d]] = pos; // 尝试表示每个回文串最后的位置。(删掉也对)
        las = ch[u][d];
        cnt[las]++;
    }

    int arr[PAMN][26];

    ll solve() {
    
    
        ll ans = 0;

        for (int i = tot; i > 1; i--) {
    
    
            cnt[fail[i]] += cnt[i];
        }

        for (int i = 1; i <= pos; ++i) {
    
    
            memcpy(arr[i], arr[i - 1], sizeof arr[0]);
            arr[i][s[i] - 'a']++;
        }

        for (int i = 2, l, r; i <= tot; i++) {
    
    
            ll tmp = 0;
            r = npos[i];
            l = r - len[i];
            for (int j = 0; j < 26; ++j) {
    
    
                tmp += ll(arr[l][j] < arr[r][j]);
            }
            ans += tmp * cnt[i];
        }
        return ans;
    }
};  // namespace pam

char s[PAMN];

int main() {
    
    
    PAM::init();
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for (int i = 1; i <= n; i++) {
    
    
        PAM::extend(s[i]);
    }
    printf("%lld\n", PAM::solve());
    return 0;
}

struct版本

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int PAMN = 3e5 + 59;

struct PAM {
    
    
    int tot, las;
    int ch[PAMN][26];
    int cnt[PAMN];
    int len[PAMN];
    int fail[PAMN];
    int npos[PAMN];

    int pos;
    char s[PAMN];

    int node(int l) {
    
    
        tot++;
        memset(ch[tot], 0, sizeof(ch[tot]));
        len[tot] = l;
        cnt[tot] = 0;
        fail[tot] = 0;
        npos[tot] = pos;
        return tot;
    }

    void init() {
    
    
        tot = -1;
        las = 0;
        pos = 0;
        s[pos] = '$';   // s = '$......'
        node(0);      // node[0] : len =  0 偶根
        node(-1);     // node[1] : len = -1 奇根
        fail[0] = 1;
    }

    int jump(int u) {
    
    
        while (s[pos - len[u] - 1] != s[pos]) {
    
    
            u = fail[u];
        }
        return u;
    }

    void extend(char c) {
    
    
        s[++pos] = c;
        int d = c - 'a';
        int u = jump(las);
        if (!ch[u][d]) {
    
    
            int v = node(len[u] + 2);
            fail[v] = ch[jump(fail[u])][d];
            ch[u][d] = v;
        }
        las = ch[u][d];
        cnt[las]++;
    }
} pam;

char s[PAMN];
int arr[PAMN][26];


int main() {
    
    
    pam.init();
    scanf("%s", s + 1);
    int n = strlen(s + 1);
    for (int i = 1; i <= n; i++) {
    
    
        pam.extend(s[i]);
    }

    ll ans = 0;

    for (int i = pam.tot; i > 1; i--) {
    
    
        pam.cnt[pam.fail[i]] += pam.cnt[i];
    }

    for (int i = 1; i <= n; ++i) {
    
    
        memcpy(arr[i], arr[i - 1], sizeof arr[0]);
        arr[i][s[i] - 'a']++;
    }

    for (int i = 2, l, r; i <= pam.tot; i++) {
    
    
        ll tmp = 0;
        r = pam.npos[i];
        l = r - pam.len[i];
        for (int j = 0; j < 26; ++j) {
    
    
            tmp += ll(arr[l][j] < arr[r][j]);
        }
        ans += tmp * pam.cnt[i];
    }

    printf("%lld\n", ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Tighway/article/details/108654496
pam