Live2D

Solution -「LOJ #141」回文子串 ||「模板」双向 PAM

Description

  Link.

  给定字符串 s,处理 q 次操作:

  1. s 前添加字符串;
  2. s 后添加字符串;
  3. s 的所有非空回文子串数目。

  任意时刻 |s|4×105q105

Solution

  双向 PAM 模板题。

  思考一个正常的 PAM 所维护的——一个 DFA,每个结点的连边代表左右各加同一个字符;还有一个 fail 树,连向结点的最长回文后缀(当然也就是最长回文前缀)。在双向 PAM 也是一个道理,增量法构造过程中顺便处理 fail 树深度和即可。

  复杂度 O(|s|+q)

Solution

/*~Rainybunny~*/

#include <cstdio>
#include <cstring>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

typedef long long LL;

const int MAXN = 4e5, MAXL = 7e5;
char s[MAXL + 10];
int ptrf, ptrb;

struct PalindromeAutomaton {
    int node, len[MAXN + 5], fail[MAXN + 5], ch[MAXN + 5][26];
    int rlas, llas, dep[MAXN + 5];

    PalindromeAutomaton() { node = rlas = llas = 1, len[1] = -1, fail[0] = 1; }

    inline int pushF( char c ) {
        s[--ptrf] = c, c -= 'a'; int p = llas;
        for ( ; s[ptrf + len[p] + 1] != s[ptrf]; p = fail[p] );
        if ( !ch[p][c] ) {
            len[++node] = len[p] + 2; int q = fail[p];
            for ( ; s[ptrf + len[q] + 1] != s[ptrf]; q = fail[q] );
            dep[node] = dep[fail[node] = ch[q][c]] + 1, ch[p][c] = node;
        }
        llas = ch[p][c];
        if ( len[llas] == ptrb - ptrf + 1 ) rlas = llas;
        return dep[llas];
    }

    inline int pushB( char c ) {
        s[++ptrb] = c, c -= 'a'; int p = rlas;
        for ( ; s[ptrb - len[p] - 1] != s[ptrb]; p = fail[p] );
        if ( !ch[p][c] ) {
            len[++node] = len[p] + 2; int q = fail[p];
            for ( ; s[ptrb - len[q] - 1] != s[ptrb]; q = fail[q] );
            dep[node] = dep[fail[node] = ch[q][c]] + 1, ch[p][c] = node;
        }
        rlas = ch[p][c];
        if ( len[rlas] == ptrb - ptrf + 1 ) llas = rlas;
        return dep[rlas];
    }
} pam;

int main() {
    ptrf = ( ptrb = 3e5 ) + 1;
    LL ans = 0;
    for ( char c; 'a' <= ( c = getchar() ) && c <= 'z';
      ans += pam.pushB( c ) );

    int q, op; char tmp[1005];
    for ( scanf( "%d", &q ); q--; ) {
        scanf( "%d", &op );
        if ( op == 1 ) {
            scanf( "%s", tmp );
            for ( int i = 0; tmp[i]; ans += pam.pushB( tmp[i++] ) );
        } else if ( op == 2 ) {
            scanf( "%s", tmp );
            for ( int i = 0; tmp[i]; ans += pam.pushF( tmp[i++] ) );
        } else {
            printf( "%lld\n", ans );
        }
    }
    
    return 0;
}

posted @   Rainybunny  阅读(131)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2020-07-13 Solution -「SV 2020 Round I」SA
点击右上角即可分享
微信分享提示