\(\mathcal{Description}\)

  Link.

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

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

  任意时刻 \(|s|\le4\times10^5\),\(q\le10^5\)。

\(\mathcal{Solution}\)

  双向 PAM 模板题。

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

  复杂度 \(\mathcal O(|s|+q)\)。

\(\mathcal{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;
}

最新文章

  1. 2-Spark高级数据分析-第二章 用Scala和Spark进行数据分析
  2. 【转】android 属性动画之 ObjectAnimator
  3. 快速卸载VS2015的办法
  4. unity shader random number
  5. android listview和button,ImageButton等有事件的控件的总结
  6. amd(超微半导体公司(英语:Advanced Micro Devices, Inc.,缩写:AMD))
  7. X265编码效率仍然低
  8. Tomcat基础教程(四)
  9. C#程序中将图片转换为byte数组,并将byte数组转换为图片
  10. ZOJ 1649:Rescue(BFS)
  11. “HK”的日常之ARP断网攻击
  12. TCP粘包和拆包问题
  13. [Python]range与xrange用法对比
  14. 2019春第九周作业Compile Summarize
  15. ES搜索结果调优
  16. Excel函数之sumifs应用
  17. Scrapy爬虫入门实例
  18. [IOS]开源库RegexKitLite正则表达式的使用
  19. 机器学习算法实现解析——word2vec源代码解析
  20. CentOS 7防火墙开放端口快速方法

热门文章

  1. Linux上天之路(十三)之系统进程管理
  2. vue2.0与vue3.0项目创建
  3. 360浏览器兼容模式下jsp页面访问不到js文件
  4. 《剑指offer》面试题53 - II. 0~n-1中缺失的数字
  5. 阐述JDBC操作数据库的步骤
  6. 「DP 浅析」斜率优化
  7. DispatcherServlet的init源代码
  8. 只要一行代码,实现五种 CSS 经典布局
  9. 鸿蒙轻内核M核源码分析:LibC实现之Musl LibC
  10. request.getServletContext()爆红问题