【bzoj3676】[Apio2014]回文串 —— 回文自动机的学习
写题遇上一棘手的题,[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
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]回文串 —— 回文自动机的学习的更多相关文章
- [Bzoj3676][Apio2014]回文串(后缀自动机)(parent树)(倍增)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 3396 Solved: 1568[Submit][Statu ...
- 2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)
传送门 对原串建立一个后缀自动机,然后用反串在上面匹配. 如果当前匹配的区间[l,r][l,r][l,r]包裹了当前状态的endposendposendpos中的最大值,那么[l,maxpos][l, ...
- 【bzoj3676】[Apio2014]回文串 回文自动机
题目描述 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最大出现值. 输入 输入只有一行,为一个只包含小写字母( ...
- BZOJ3676[Apio2014]回文串——回文自动机
题目描述 考虑一个只包含小写拉丁字母的字符串s.我们定义s的一个子串t的“出 现值”为t在s中的出现次数乘以t的长度.请你求出s的所有回文子串中的最 大出现值. 输入 输入只有一行,为一个只包含小写字 ...
- [bzoj3676][Apio2014]回文串——Manacher+后缀自动机+倍增
Brief Description 一个回文串的value定义为这个回文串的长度乘以出现次数.给定一个字符串,求\(value_{max}\). Algorithm Design 我们使用Manach ...
- bzoj 3676: [Apio2014]回文串 回文自动机
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 844 Solved: 331[Submit][Status] ...
- bzoj 2565: 最长双回文串 回文自动机
题目: Description 顺序和逆序读起来完全一样的串叫做回文串.比如acbca是回文串,而abc不是(abc的顺序为"abc",逆序为"cba",不相同 ...
- hysbz3676 回文串 回文自动机
回文自动机模板题 头铁了一下午hdu6599,最后发现自己的板有问题 先放这里一个正确性得到基本确认的板,过两天肝hdu6599 #pragma GCC optimize(2) #include< ...
- HYSBZ 3676 回文串 (回文树)
3676: [Apio2014]回文串 Time Limit: 20 Sec Memory Limit: 128 MB Submit: 1680 Solved: 707 [Submit][Stat ...
随机推荐
- python selenium等待特定网页元素加载完毕
selenium等待特定元素加载完毕 is_disappeared = WebDriverWait(driver, 8, 0.5, ignored_exceptions=TimeoutExceptio ...
- promise的简单使用
var p = new Promise(function (resolve,reject) { /*setTimeout(function () { resolve('success') },3000 ...
- Linux高阶命令进阶
1. 输出重定向 > (1>):覆盖输出,会覆盖掉原先的文件内容 >> (1>>) :追加输出,不会覆盖原始文件内容,会在原始内容末尾继续添加 2> :错误输 ...
- Linux中find命令用法大全
Linux 查找命令是Linux系统中最重要和最常用的命令之一.查找用于根据与参数匹配的文件指定的条件来搜索和查找文件和目录列表的命令.查找可以在各种条件下使用,您可以通过权限,用户,组,文件类型,日 ...
- Python数据类型方法
Python认为一切皆为对象:比如我们初始化一个list时: li = list('abc') 实际上是实例化了内置模块builtins(python2中为__builtin__模块)中的list类: ...
- Mongo索引学习笔记
索引使用场景 优:加快查询速度 劣:增删改会产生额外的开销.占用空间 tips: 返回集合中一半以上的数据,全表扫描的效率高 索引基础 基础操作 查看索引:db.test.getIndexes() 创 ...
- 【codeforces 1109B】Sasha and One More Name
[链接] 我是链接,点我呀:) [题意] 题意 [题解] 如果这个回文串的左半部分,字母全是一样的. 那么显然不可能再分出来了,因为不管怎么分怎么排列,最后肯定都只能和原串一样. 所以无解 其他情况下 ...
- CodeForcesGym 100524A Astronomy Problem
Astronomy Problem Time Limit: 8000ms Memory Limit: 524288KB This problem will be judged on CodeForce ...
- 权限管理组件:rbac
rbac: Role_Based Access Control,基于角色的权限控制 权限:一个包含正则表达式 的url就是一个权限 目录结构: rbac这个app中的文件代码如下: rbac/mode ...
- [NOIP2007] 提高组 洛谷P1005 矩阵取数游戏
题目描述 帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij均为非负整数.游戏规则如下: 1.每次取数时须从每行各取走一个元素,共n个.m次后取完矩阵所有元素: 2. ...