回文自动机例题

今天学习了一下回文自动机,吊打\(manacher\)有没有(除了空间
回文自动机基于这两个性质:
1.一个长度为\(n\)的字符串的本质不同回文子串是\(O(n)\)级别的
2.在一个字符串后增加一个字符后,最多新增\(1\)个本质不同回文子串
这两条性质都可以用归纳法证明
于是我们想到用一个结点来代表一个本质不同的回文串,然后就有\(PAM\)了。和\(AC\)自动机一样,\(PAM\)也有\(fail\)失配边和\(ch\)转移边,\(fail\)代表的是最长回文后缀
采用增量构造法来构建\(PAM\),在当前字符串末尾插入一个字符\(c\),如果出现了一个新的回文子串,就新建一个结点,并计算其失配边
另外最开始有两个结点,分别代表奇数长度的根结点和偶数长度的根结点
代码如下(细节看注释):

#define N 100000

int last, nid;
int ch[N+5][26], fail[N+5], len[N+5], cnt[N+5];

void init() {
  fail[0] = fail[1] = nid = 1; //偶数的根结点为0,fail指向1;奇数为1,fail指向自己
  len[1] = -1; //因为是奇数,所以不是0
}

void build(char *s) {
  int n = strlen(s+1);
  for(int i = 1; i <= n; ++i) {
    int p = last, c = s[i]-'a';
    while(s[i-len[p]-1] != s[i]) p = fail[p]; //直到能够形成回文串
    if(!ch[p][c]) { //新的本质不同回文串
      int v = ++nid, t = fail[p]; //把t设为fail[p]是因为后缀不能是它本身
      len[v] = len[p]+2;
      while(s[i-len[t]-1] != s[i]) t = fail[t]; //跳fail找最长回文后缀
      fail[v] = ch[t][c]; //跟AC自动机有点像
      ch[p][c] = v;
    }
    last = ch[p][c]; //更新last
    cnt[last]++;
  }
}

然后[APIO2014]回文串就变成板子了:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

#define N 300000

int last, nid;
int ch[N+5][26], fail[N+5], len[N+5], cnt[N+5];
long long ans = 0;

void init() {
  fail[0] = fail[1] = nid = 1;
  len[1] = -1;
}

void build(char *s) {
  int n = strlen(s+1);
  for(int i = 1; i <= n; ++i) {
    int p = last, c = s[i]-'a';
    while(s[i-len[p]-1] != s[i]) p = fail[p];
    if(!ch[p][c]) {
      int v = ++nid, t = fail[p];
      len[v] = len[p]+2;
      while(s[i-len[t]-1] != s[i]) t = fail[t];
      fail[v] = ch[t][c];
      ch[p][c] = v;
    }
    last = ch[p][c];
    cnt[last]++;
  }
}

void topo() {
  queue<int> q;
  int in[N+5] = {0};
  for(int i = 0; i <= nid; ++i) in[fail[i]]++;
  for(int i = 0; i <= nid; ++i) if(!in[i]) q.push(i);
  while(!q.empty()) {
    int u = q.front(); q.pop();
    ans = max(ans, 1LL*len[u]*cnt[u]);
    in[fail[u]]--, cnt[fail[u]] += cnt[u];
    if(!in[fail[u]]) q.push(fail[u]);
  }
}

int main() {
  init();
  char s[N+5] = {0};
  scanf("%s", s+1);
  build(s);
  topo();
  printf("%lld\n", ans);
  return 0;
}

还有一道最长双回文串

原文地址:https://www.cnblogs.com/dummyummy/p/10756970.html

时间: 2024-11-07 13:25:46

回文自动机例题的相关文章

回文树或者回文自动机,及相关例题

回文树简述 在大部分说法中,回文树与回文自动机指的是一个东西: 回文树是对一个字符串,基于自动机思想构建的处理回文问题的树形结构: 回文树是对着一个单串建立的: 于是他主要用于计数(回文子串种类及个数) 基本建立思路是先建立其前缀的回文树,然后每加上一个字符,统计影响: 回文树存在fail指针但一般不承接字符串匹配问题: (回文树大概可以判定一个回文串是不是一个串的子串,但KMP之类的可以做得更好) 构建好的回文树,是这样的: (好难看) 可看出: 存在两个树结构,分别记录奇数|偶数长度的回文:

Palindromic Tree 回文自动机-回文树 例题+讲解

---恢复内容开始--- 回文树,也叫回文自动机,是2014年被西伯利亚民族发明的,其功能如下: 1.求前缀字符串中的本质不同的回文串种类 2.求每个本质不同回文串的个数 3.以下标i为结尾的回文串个数/种类 4.每个本质不同回文串包含的本质不同回文串种类 (本文参考自Palindromic Tree——回文树[处理一类回文串问题的强力工具],Palindromic Tree 回文自动机-回文树 解决回文串的神器) 下面介绍一些数组的意义 next[][]类似于字典树,指向当前字符串在两段同时加

【回文自动机】bzoj3676 [Apio2014]回文串

回文自动机讲解!http://blog.csdn.net/u013368721/article/details/42100363 pam上每个点代表本质不同的回文子串.len(i)代表长度,cnt(i)代表个数(要最后在fail树上dp一遍方可). 答案直接枚举一遍结点,然后用len(i)*cnt(i),取最大者即可. 回文自动机是非常优越的数据结构,可惜比manacher多一个字符集的空间-- #include<cstdio> #include<cstring> #include

【bzoj3676】[Apio2014]回文串 回文自动机

题目描述 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最大出现值. 输入 输入只有一行,为一个只包含小写字母(a -z)的非空字符串s. 输出 输出一个整数,为逝查回文子串的最大出现值. 样例输入 [样例输入l] abacaba [样例输入2] www 样例输出 [样例输出l] 7 [样例输出2] 4 题解 回文自动机裸题 关于PAM个人暂时理解不是很深入,挖坑待填. 本题只需要统计fail树的子树大小,再

[BZOJ4044]Virus synthesis 回文自动机的DP

4044: [Cerc2014] Virus synthesis Time Limit: 20 Sec  Memory Limit: 128 MB Description Viruses are usually bad for your health. How about fighting them with... other viruses? In this problem, you need to find out how to synthesize such good viruses. W

【CF932G】Palindrome Partition 回文自动机

[CF932G]Palindrome Partition 题意:给你一个字符串s,问你有多少种方式,可以将s分割成k个子串,设k个子串是$x_1x_2...x_k$,满足$x_1=x_k,x_2=x_{k-1}...x_i=x{k-i+1}$. $|s|\le 10^6$ 题解:设字符串的长度为n,考虑字符串$T=s_1s_ns_2s_{n-1}...$.问题就转化成了:求将原串划分成若干个长度为偶数的回文子串的方案数. 首先我们有一种暴力的想法,设f[i]表示将前i个字符分成若干个回文子串的方

【XSY2715】回文串 树链剖分 回文自动机

题目描述 有一个字符串\(s\),长度为\(n\).有\(m\)个操作: \(addl ~c\):在\(s\)左边加上一个字符\(c\) \(addr~c\):在\(s\)右边加上一个字符 \(transl~l_1~r_1~l_2~r_2\):有两个\(s\)的子串\(s_1=s[l_1\ldots r_1],s_2=s[l_2\ldots r_2]\).你要把\(s_1\)变成\(s_2\).每次允许在左边加一个字符或删一个字符.要求操作次数最少.定义一个字符串是好的当且仅当这个字符串是回文串

WHU 583 Palindrome ( 回文自动机 &amp;&amp; 本质不同的回文串的个数 )

题目链接 题意 : 给你一个串.要你将其划分成两个串.使得左边的串的本质不同回文子串的个数是右边串的两倍.对于每一个这样子的划分.其对答案的贡献就是左边串的长度.现在要你找出所有这样子的划分.并将贡献乘起来.答案 mod 1e9+7 分析 : 从左到右跑一边回文自动机.对于每个前缀 能够得出其有多少个本质不同的回文子串 本质不同的回文子串的个数实际上就是自动机节点数 - 2 那么跑一遍前缀之后我们能得到所有可作为左边部分串的本质不同回文子串的个数 因为是回文串.所以我们倒着跑一遍.就同样能得到作

hysbz3676 回文串 回文自动机

回文自动机模板题 头铁了一下午hdu6599,最后发现自己的板有问题 先放这里一个正确性得到基本确认的板,过两天肝hdu6599 #pragma GCC optimize(2) #include<bits/stdc++.h> #include<iostream> #include<cstring> #include<cassert> #define MAXN 300010 #define LL long long #define BASE 2LL #defi