http://acm.hdu.edu.cn/showproblem.php?pid=6599

有好几种实现方式,首先都是用回文自动机统计好回文串的个数。

记得把每个节点的cnt加到他的fail上,因为他既然出现了那么他的fail也当然会出现。

这里需要一直从fail向上找到一个长度恰好一半的节点,这也是TLE的来源,重复跳了大量的指针。dalao给了一种实现。

额外维护一个x数组,假如x[i]==0,说明还没人动过它,x[i]=i。

然后沿着fail把x[i]向上移动到第一个>=它的长度的一半的fail祖先,验证是不是长度恰好一半。

最后把i的fail父亲的x指针指向x[i],也就是把它的fail父亲直接x指向i的fail祖先,这样它父亲就不需要经过一系列没有用的fail转移了。

这个是个“不那么彻底的”路径压缩。源于一个点s的fail父f,f需要用的fail祖先必定是s的祖先,s可以每次把它父亲f一起给移动了。

当然也有hash大法、manacher大法。

hash大法好!

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; struct Node {
int len, ch[26], fail;
ll cnt, x, y;
//string str;
Node(int len = 0) : len(len), fail(0) {
memset(ch, 0, sizeof(ch));
//下面是维护额外信息
cnt = 0;
x = 0;
y = 0;
//str = "";
}
/*void show() {
printf(" str=\"%s\"\n", str.c_str());
printf(" len=%d cnt=%d\n", len, cnt);
}*/
}; const int MAXN = 300000; ll ans2[MAXN + 5]; //PalindromicAutomaton
struct PAM {
Node nd[MAXN + 5]; int len, top, last; // len为字符串长度mtop为节点个数,last为最后插入字符所对应的节点
char s[MAXN + 5];
//string ls; //用来展示的辅助字符串 int getfail(int x) { //沿着fail指针找到第一个回文后缀
while(s[len - nd[x].len - 1] != s[len])
x = nd[x].fail;
return x;
} void init() {
len = 0, top = 0, last = 0;
nd[top] = Node(0);
nd[top].fail = 1;
nd[++top] = Node(-1);
nd[top].fail = 0;
s[0] = '$';
} void extend(char c) {
s[++len] = c;
int now = getfail(last); //找到插入的位置
//ls = nd[now].str + c; //用来展示的辅助字符串
if(!nd[now].ch[c - 'a']) { //若没有这个节点,则新建并求出它的fail指针
nd[++top] = Node(nd[now].len + 2);
nd[top].fail = nd[getfail(nd[now].fail)].ch[c - 'a'];
nd[now].ch[c - 'a'] = top;
}
last = nd[now].ch[c - 'a'];
//nd[last].str = ls; //用来展示的辅助字符串
//下面是维护额外信息
++nd[last].cnt;
} /*void show() {
for(int i = top; i >= 0; --i) {
printf("node: id=%d\n", i);
nd[i].show();
printf("fail: id=%d\n", nd[i].fail);
nd[nd[i].fail].show();
puts("");
}
}*/ void count() {
for(int i = top; i >= 2; --i)
nd[nd[i].fail].cnt += nd[i].cnt;
} void solve() {
for(int i = top; i >= 2; --i) {
if(nd[i].x == 0)
nd[i].x = i;
while(nd[nd[i].x].len > (nd[i].len + 1) / 2 )
nd[i].x = nd[nd[i].x].fail;
if(nd[nd[i].x].len == (nd[i].len + 1) / 2)
nd[i].y = 1;
nd[nd[i].fail].x = nd[i].x;
}
} void ans(int n) {
memset(ans2, 0, sizeof(ans2[1]) * (n + 1));
for(int i = top; i >= 2; --i) {
ans2[nd[i].len] += nd[i].cnt * nd[i].y;
}
for(int i = 1; i <= n; ++i) {
printf("%lld%c", ans2[i], " \n"[i == n]);
}
}
} pam; char s[MAXN + 5]; int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
while(~scanf("%s", s)) {
int n = strlen(s);
pam.init();
for(int i = 0; s[i] != '\0'; ++i)
pam.extend(s[i]);
//pam.show();
pam.count();
pam.solve();
pam.ans(n);
}
return 0;
}

2019 Multi-University Training Contest 2 - 1009 - 回文自动机的更多相关文章

  1. 2019 Multi-University Training Contest 2 I.I Love Palindrome String(回文自动机+字符串hash)

    Problem Description You are given a string S=s1s2..s|S| containing only lowercase English letters. F ...

  2. 2019牛客暑期多校训练营(第六场)C - Palindrome Mouse (回文自动机)

    https://ac.nowcoder.com/acm/contest/886/C 题意: 给出一个串A , 集合S里面为A串的回文字串 , 现在在集合S里面找出多少对(a,b),b为a的字串 分析: ...

  3. [2019杭电多校第二场][hdu6599]I Love Palindrome String(回文自动机&&hash)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6599 题目大意为求字符串S有多少个子串S[l,r]满足回文串的定义,并且S[l,(l+r)/2]也满足 ...

  4. hdu多校第二场1009 (hdu6599) I Love Palindrome String 回文自动机/字符串hash

    题意: 找出这样的回文子串的个数:它本身是一个回文串,它的前一半也是一个回文串 输出格式要求输出l个数字,分别代表长度为1~l的这样的回文串的个数 题解: (回文自动机和回文树是一个东西) 首先用回文 ...

  5. URAL 2040 Palindromes and Super Abilities 2 (回文自动机)

    Palindromes and Super Abilities 2 题目链接: http://acm.hust.edu.cn/vjudge/contest/126823#problem/E Descr ...

  6. URAL 2040 (回文自动机)

    Problem Palindromes and Super Abilities 2 (URAL2040) 题目大意 给一个字符串,从左到右依次添加,询问每添加一个字符,新增加的回文串数量. 解题分析 ...

  7. 后缀自动机/回文自动机/AC自动机/序列自动机----各种自动机(自冻鸡) 题目泛做

    题目1 BZOJ 3676 APIO2014 回文串 算法讨论: cnt表示回文自动机上每个结点回文串出现的次数.这是回文自动机的定义考查题. #include <cstdlib> #in ...

  8. [模板] 回文树/回文自动机 && BZOJ3676:[Apio2014]回文串

    回文树/回文自动机 放链接: 回文树或者回文自动机,及相关例题 - F.W.Nietzsche - 博客园 状态数的线性证明 并没有看懂上面的证明,所以自己脑补了一个... 引理: 每一个回文串都是字 ...

  9. BZOJ2160拉拉队排练——回文自动机

    题目描述 艾利斯顿商学院篮球队要参加一年一度的市篮球比赛了.拉拉队是篮球比赛的一个看点,好的拉拉队往往能帮助球队增加士气,赢得最终的比赛.所以作为拉拉队队长的楚雨荨同学知道,帮助篮球队训练好拉拉队有多 ...

随机推荐

  1. 35.ES6语法介绍——2019年12月24日

    2019年12月24日16:22:24 2019年10月09日12:04:44 1. ES6介绍 1.1 新的 Javascript 语法标准 --2015年6月正式发布 --使用babel语法转换器 ...

  2. SpringBoot---Kafka

    1.实战 <!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka --> <dependency> < ...

  3. iOS---如何获取手机的本地照片和相册

    __weak ViewController *weakSelf = self; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIO ...

  4. Ubuntu 16.04下使用docker部署Redis主从复制

    (以下docker相关的命令,需要在root用户环境下或通过sudo提升权限来进行操作.) 1.拉取Redis镜像到本地 docker pull redis 2. 准备Redis主从配置文件 Redi ...

  5. Laya 使list渲染支持分帧的思路

    Laya 使list渲染支持分帧的思路 @author ixenos 2019-09-06 1.由于Laya的list渲染时没有做分帧处理,只做了延迟帧处理,所以当单页元素较多时,会有大量运算卡帧的情 ...

  6. 【PowerOJ1744&网络流24题】方格取数问题(最小割)

    题意: n,m<=30 思路: [问题分析] 二分图点权最大独立集,转化为最小割模型,从而用最大流解决. [建模方法] 首先把棋盘黑白染色,使相邻格子颜色不同,所有黑色格子看做二分图X集合中顶点 ...

  7. javascript中面向对象的两种构建方式(构造函数)和(原型模式的区别)

    1.构造函数模式--->alert的结果为false <!DOCTYPE html> <html lang="en"> <head> &l ...

  8. Why Use the Widget Factory?

    https://learn.jquery.com/jquery-ui/widget-factory/why-use-the-widget-factory/ Writing jQuery plugins ...

  9. java valueOf

    valueOf 方法可以将原生数值类型转化为对应的Number类型,java.lang.Number 基类包括ouble.Float.Byte.Short.Integer 以及 Long派生类, 也可 ...

  10. Java 关键字列表

    字 描述 abstract 抽象方法,抽象类的修饰符 assert 断言条件是否满足 boolean 布尔数据类型 break 跳出循环或者label代码段 byte 8-bit 有符号数据类型 ca ...