写题遇上一棘手的题,[Apio2014]回文串,一眼看过后缀数组+Manacher。然后就码码码。。。过是过了,然后看一下[Status],怎么慢这么多,不服。。然后就搜了一下,发现一种新东西——回文树。

回文树的功能:

假设我们有一个串S,S下标从0开始,则回文树能做到如下几点:

1.求串S前缀0~i内本质不同回文串的个数(两个串长度不同或者长度相同且至少有一个字符不同便是本质不同)

2.求串S内每一个本质不同回文串出现的次数

3.求串S内回文串的个数(其实就是1和2结合起来)

4.求以下标i结尾的回文串的个数

构造回文树

首先定义变量

1.len[i] 表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串)

2.next[i][c] 表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号(和字典树类似)

3.fail[i] 表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串(和AC自动机类似)

4.cnt[i] 表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)

5.num[i] 表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数

6.last 指向新添加一个字母后所形成的最长回文串表示的节点。

7.S[i] 表示第i次添加的字符(一开始设S[0] = -1(可以是任意一个在串S中不会出现的字符))

8.p 表示添加的节点个数

9.n表示添加的字符个数

一开始回文树有两个节点,0表示偶数长度串的根和1表示奇数长度串的根,且len[0]=0,len[1]=-1,last=0,S[0]=-1,n=0,p=2(添加了节点0、1)。

假设现在我们有串S = abbaabba。

首先我们添加第一个字符'a',S[++n]='a',然后判断此时S[n-len[last]-1]是否等于S[n],即上一个串-1的位置和新添加的位置是否相同,相同则说明构成回文。

否则,last = fail[last]。

此时last=0,我们发现S[1-0-1]!=S[1],所以last=fail[last]=1,然后我们发现S[1-(-1)-1] == S[1](即自己等于自己,所以我们让len[1]等于-1可以让这一步更加方便)。

令cur等于此时的last(即 cur=last=1),判断此时next[cur]['a']是否已经有后继。

如果next[cur]['a']没有后继,我们就进行如下的步骤:

新建节点(节点数p++,且之后p=3),并让now等于新节点的编号(now=2),则len[now]=len[cur]+2(每一个回文串的长度总是在其最长子回文串的基础上在两边加上两个相同的字符构成的,所以是+2,同时体现出我们让len[1]=-1的优势,一个字符自成一个奇回文串时回文串的长度为(-1)+2=1)。

然后我们让fail[now]=next[get_fail(fail[cur] )]['a'],即得到fail[now](此时为fail[2]=0),其中的get_fail函数就是让找到第一个使得S[n-len[last]-1]==S[n]的last。

然后next[cur]['a']=now。

当上面步骤完成后我们让last=next[cur][c](不管next[cur]['a']是否有后继),然后cnt[last] ++。

此时回文树为下图状态:

现在我们添加第二个字符字符'b'到回文树中:

继续添加第三个字符'b'到回文树中:

继续添加第四个字符'a'到回文树中:

继续添加第五个字符'a'到回文树中:

继续添加第六个字符'b'到回文树中:

继续添加第七个字符'b'到回文树中:

继续添加第八个字符'a'到回文树中:

到此,串S已经完全插入到回文树中了,现在所有的数据如下:

然后我们将节点x在fail指针树中将自己的cnt累加给父亲,从叶子开始倒着加,最后就能得到串S中出现的每一个本质不同回文串的个数。

构造回文树需要的空间复杂度为O(N*字符集大小),时间复杂度为O(N*log(字符集大小)),这个时间复杂度比较神奇。如果空间需求太大,可以改成邻接表的形式存储,不过相应的要牺牲一些时间。

总的来说,这是一个很好的算法

模板:

 const int MAXN =  ;
const int N = ; struct Palindromic_Tree {
int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
int cnt[MAXN] ;
int num[MAXN] ;
int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
int S[MAXN] ;//存放添加的字符
int last ;//指向上一个字符所在的节点,方便下一次add
int n ;//字符数组指针
int p ;//节点指针 int newnode ( int l ) {//新建节点
for ( int i = ; i < N ; ++ i ) next[p][i] = ;
cnt[p] = ;
num[p] = ;
len[p] = l ;
return p ++ ;
} void init () {//初始化
p = ;
newnode ( ) ;
newnode ( - ) ;
last = ;
n = ;
S[n] = - ;//开头放一个字符集中没有的字符,减少特判
fail[] = ;
} int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
while ( S[n - len[x] - ] != S[n] ) x = fail[x] ;
return x ;
} void add ( int c ) {
c -= 'a' ;
S[++ n] = c ;
int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
int now = newnode ( len[cur] + ) ;//新建节点
fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
next[cur][c] = now ;
num[now] = num[fail[now]] + ;
}
last = next[cur][c] ;
cnt[last] ++ ;
} void count () {
for ( int i = p - ; i >= ; -- i ) cnt[fail[i]] += cnt[i] ;
//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
}
} ;

Palindromic Tree

下面是一些题目,感兴趣的话可以做一下~

1.ural1960. Palindromes and Super Abilities

2.TsinsenA1280. 最长双回文串

3.TsinsenA1255. 拉拉队排练

4.TsinsenA1393. Palisection

5.2014-2015 ACM-ICPC, Asia Xian Regional Contest G The Problem to Slow Down You

转载 from:http://blog.csdn.net/u013368721/article/details/42100363

接下来是【Apio2014】回文串

 /**************************************************************
Problem: 3676
User: YJY
Language: C++
Result: Accepted
Time:756 ms
Memory:46712 kb
****************************************************************/ #include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std; typedef long long LL; #define N 330010 int next[N][];
int fail[N];
int cnt[N];
int num[N];
int len[N];
int s[N];
int last;
int p;
int n; char str[N]; LL ans=; int newnode(int x)
{
for (int i=;i<;i++)
next[p][i]=;
cnt[p]=;
num[p]=;
len[p]=x;
return p++;
} void init()
{
p=;
newnode();
newnode(-);
last=;
n=;
s[]=-;
fail[]=;
} int get_fail(int x)
{
while (s[n-len[x]-]!=s[n])
x=fail[x];
return x;
} void add(int c)
{
c-='a';
s[++n]=c;
int cur=get_fail(last);
if (!next[cur][c])
{
int now=newnode(len[cur]+);
fail[now]=next[get_fail(fail[cur])][c];
next[cur][c]=now;
num[now]=num[fail[now]]+;
}
last=next[cur][c];
cnt[last]++;
} void count()
{
for (int i=p-;i>=;i--)
cnt[fail[i]]+=cnt[i];
} int main()
{
scanf("%s",str);
init();
int sz=strlen(str);
for (int i=;i<sz;i++)
add(str[i]);
count();
for (int i=;i<p;i++)
ans=max(ans,1LL*len[i]*cnt[i]);
printf("%lld\n",ans);
return ;
}

【bzoj3676】[Apio2014]回文串 —— 回文自动机的学习的更多相关文章

  1. [Bzoj3676][Apio2014]回文串(后缀自动机)(parent树)(倍增)

    3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 3396  Solved: 1568[Submit][Statu ...

  2. 2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)

    传送门 对原串建立一个后缀自动机,然后用反串在上面匹配. 如果当前匹配的区间[l,r][l,r][l,r]包裹了当前状态的endposendposendpos中的最大值,那么[l,maxpos][l, ...

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

    题目描述 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最大出现值. 输入 输入只有一行,为一个只包含小写字母( ...

  4. BZOJ3676[Apio2014]回文串——回文自动机

    题目描述 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. 输入 输入只有一行,为一个只包含小写字 ...

  5. [bzoj3676][Apio2014]回文串——Manacher+后缀自动机+倍增

    Brief Description 一个回文串的value定义为这个回文串的长度乘以出现次数.给定一个字符串,求\(value_{max}\). Algorithm Design 我们使用Manach ...

  6. bzoj 3676: [Apio2014]回文串 回文自动机

    3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 844  Solved: 331[Submit][Status] ...

  7. bzoj 2565: 最长双回文串 回文自动机

    题目: Description 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为"abc",逆序为"cba",不相同 ...

  8. hysbz3676 回文串 回文自动机

    回文自动机模板题 头铁了一下午hdu6599,最后发现自己的板有问题 先放这里一个正确性得到基本确认的板,过两天肝hdu6599 #pragma GCC optimize(2) #include< ...

  9. HYSBZ 3676 回文串 (回文树)

    3676: [Apio2014]回文串 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 1680  Solved: 707 [Submit][Stat ...

随机推荐

  1. tinyxml

    在TinyXML中,根据XML的各种元素来定义了一些类:        TiXmlBase:整个TinyXML模型的基类.        TiXmlAttribute:对应于XML中的元素的属性.   ...

  2. 简述FTP主动模式与被动模式

    1 FTP工作模式 2 不同模式FTP面临的问题 3 主动模式的FTP连接建立连接主要步骤 客户端打开一个随机的端口(端口号大于1024,在这里,我们称它为x),同时一个FTP进程连接至服务器的21号 ...

  3. awk输出指定列

    awk '{print $0} file' #打印所有列awk '{print $1}' file #打印第一列 awk '{print $1, $3}' file #打印第一和第三列 cat fil ...

  4. java中检测-在运行时指定对象是否是特定类的一个实例---关键字 instanceof

    java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例.instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例. if(requ ...

  5. xtu read problem training 4 B - Multiplication Puzzle

    Multiplication Puzzle Time Limit: 1000ms Memory Limit: 65536KB This problem will be judged on PKU. O ...

  6. hihoCoder#1082 然而沼跃鱼早就看穿了一切

    原题地址 字符串匹配+替换 注意替换串和原串长度是不等的,所以替换完还要进行收缩 可以顺带练习一下KMP 代码: #include <iostream> #include <cstr ...

  7. ZOJ 3329 期望DP

    题目大意: 给定3个已经规定好k1,k2,k3面的3个色子,如果扔到a,b,c则重新开始从1 计数,否则不断叠加所有面的数字之和,直到超过n,输出丢的次数的数学期望 我们在此令dp[]数组记录从当前数 ...

  8. [luoguP1388] 算式(DP)

    传送门 看这个n<=15本以为是个状压DP 还是too young 这个题最神奇的地方是加括号是根据贪心的策略. 发现只有在一连串的加号两边加上括号才是最优的(想一想,为什么?) f[i][j] ...

  9. j简单的分类实现-K近邻

          dataSetSize=dataSet.shape[0]                          voteIlabel=labels[sortedDistIndicies[i]] ...

  10. MTK TP手势添加

    old: #include "tpd.h" #include "tpd_custom_gt9xx.h" #ifndef TPD_NO_GPIO #include ...