写题遇上一棘手的题,[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. NET使用SuperSocket完成TCP/IP通信

    1)为什么使用SuperSocket? 性能高,易上手.有中文文档,我们可以有更多的时间用在业务逻辑上,SuperSocket有效的利用自己的协议解决粘包 2)SuperSocket的协议内容? 命令 ...

  2. 2018 CCPC 桂林站(upc复现赛)总结

    比赛一开始盯上了A题和G题,一个小时过去了还没有出题,心里有些乱.这时我看D题很多人过了,于是宝儿去看D题,说D题简单,转化成二进制暴力,于是就去做了.写的时候好像思路有点卡,WA了一发,后来马上发现 ...

  3. 色码表 Color code table

    最近打算更新设计博客页面,需要用到CSS色码表,查了一些资料现转载此处以备以后使用,点击此处查看原文,另外还发现了几个不错的网站: color-hex HTML颜色代码 色碼表 色碼表英文為 Colo ...

  4. 零基础入门学习Python(34)--丰富的else语句及简洁的with语句

    知识点 else语句的用法: 1)配合if语句 if a>b: print(a) else: print(b) 2)配合while和for循环 只在循环完成后才执行,如果循环中执行使用到brea ...

  5. c++中的三角函数

    c++中想求cos或sin: 1.首先得包含头文件,include<math.h> 2.sin(),cos(),中是弧度数,即若是角度a,则应写成cou<<sin(a*pi/1 ...

  6. Android OkHttp与物理存储介质缓存:DiskLruCache(2)

     Android OkHttp与物理存储介质缓存:DiskLruCache(2) 本文在附录文章8,9的基础之上,把Android OkHttp与DiskLruCache相结合,综合此两项技术,实 ...

  7. [luoguP1472] 奶牛家谱 Cow Pedigrees(DP)

    传送门 一个深度为i的树可以由一个根节点外加两个深度为i-1的树组成,这就决定了DP该怎么写. 然而我真的没有想到. f[i][j]表示深度为i节点数为j的个数 sum[i][j]表示深度小于等于i节 ...

  8. 安装K/3 Cloud过程中发现的两个新问题。

    卸载掉K/3 Cloud然后重装时出现下面的错误提示: 可能原因: 1.安装目录下的Setup.exe会检查操作系统版本.有些操作系统可能是被串改过注册信息,所以取不到版本信息(有些是因为盗版的原因) ...

  9. 使用流的方式去进行post请求解决中文乱码问题返回xml格式

    /** * 请求post * @Title: getHttpURLConnection * @Description: TODO(这里用一句话描述这个方法的作用) * @param: @param u ...

  10. 技术杂记之:在阿里云centos7上部署JDK MYSQL TOMCAT

    今日小编闲来无事,乘着公司新项目即将上线之际,在阿里云上整了一台centos作为测试机.原本以为一个小时搞定,结果还是花了一点小小时间.不管怎么说,记录下来,给各位小白当成课后甜点吧. 价格 先上价格 ...