写题遇上一棘手的题,[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. hdfs深入:08、hdfs的JavaAPI以及如何解决winutils的问题

    /** * 通过url注册的方式访问hdfs,了解,不会用到 * @throws Exception */ 以下为详细代码://1.注册hdfs的url,让java代码能识别hdfs的url形式URL ...

  2. 四种方案解决ScrollView嵌套ListView问题 [复制链接]

    以下文章转自@安卓泡面 在工作中,曾多次碰到ScrollView嵌套ListView的问题,网上的解决方法有很多种,但是杂而不全.我试过很多种方法,它们各有利弊. 在这里我将会从使用ScrollVie ...

  3. C++ STL容器之 stack

    STL 中的 stack 是一种容器适配器,而不是一种容器. 它是容器适配器是指,只要支持一系列方法的容器(empty, size, back, push_back, pop_back),都能作为st ...

  4. 零基础入门学习Python(7)--了不起的分支和循环1

    前言 我们今天的主题,是了不起的分支和循环,为什么不说c语言,Python了不起,而对分支和循环这两个知识点那么崇拜呢? 我们之前的几节课里也接触到了分支和循环,大家思考一下,如果我们的程序没有分支和 ...

  5. MySQL-----连表

    连表: **拿到两张表的信息** select * from userinfo,department 弊端是数据会乱,出现重复,不建议这样. **使userinfo表的part_id列与departm ...

  6. python面向对象编程设计与开发

    一.什么是面向对象的程序设计 1.何为数据结构? 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合,如列表.字典. 2.何为编程? 编程是指程序员用特定的语法+数据结构+算法,组成的代码,告 ...

  7. Xcode8、 iOS10 适配问题

    调用相机.相册.麦克风.位置等隐私问题崩溃解决办法 你的项目中访问了隐私数据,比如:相机,相册,联系人等,在Xcode8中打开编译的话,统统会crash,控制台会输出下面这样的日志: 这是因为iOS对 ...

  8. Webdriver测试脚本2(控制浏览器)

    Webdriver提供了操作浏览器的一些方法,例如控制浏览器的大小.操作浏览器前进和后退等. 控制浏览器窗口大小 有时候我们希望能以某种浏览器尺寸打开,让访问的页面在这种尺寸下运行.例如可以将浏览器设 ...

  9. PLSQLDeveloper安装与配置(详细图文)

    PLSQLDeveloper安装与配置(详细图文) 听语音 | 浏览:21912 | 更新:2016-10-24 17:12 1 2 3 4 5 6 7 分步阅读 在公司做项目时需要使用PLSQL D ...

  10. CentOS 7.1安装GNOME,开启VNC Server

    版权声明:本文为博主原创文章,未经博主允许不得转载. A.准备: 1.安装GNOME Desktop yum groupinstall 'GNOME Desktop' 2.确认GNOME Deskto ...