回文树(也就是回文自动机)实际上是奇偶两棵树,每一个节点代表一个本质不同的回文子串(一棵树上的串长度全部是奇数,另一棵全部是偶数),原串中每一个本质不同的回文子串都在树上出现一次且仅一次。

一个节点的fail指针指向它的最长回文后缀(不包括自身,所有空fail均连向1)。归纳容易证明,当在原串末尾新增一个字符时,回文树上至多会新增一个节点,这也证明了一个串本质不同的回文子串个数不会超过n。

建树时采用增量构造法,当考虑新字符s[i]时,先找到以s[i-1]为结尾的节点p,并不断跳fail。若代表新增回文子串的节点已存在则直接结束,否则通过fail[p]不断跳fail找到新节点的fail。

0,1号节点均不代表串,常数大于manacher。初始化fail[0]=fail[1]=1,len[1]=-1,tot=1,last=0。

[BZOJ2160]拉拉队排练

建立后缀树后树上DP求出每种回文子串的出现次数即可。

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=,mod=;
char s[N];
ll K,sm;
int n,ans=,lst,nd=,len[N],fail[N],son[N][],sz[N];
struct P{ int l,c; }c[N];
bool operator <(const P &a,const P &b){ return a.l>b.l; } int ksm(int a,int b){
int s=;
for (; b; a=1ll*a*a%mod,b>>=)
if (b & ) s=1ll*s*a%mod;
return s;
} void ext(int c,int n,char s[]){
int p=lst;
while (s[n-len[p]-]!=s[n]) p=fail[p];
if (!son[p][c]){
int np=++nd,q=fail[p];
while (s[n-len[q]-]!=s[n]) q=fail[q];
len[np]=len[p]+; fail[np]=son[q][c]; son[p][c]=np;
}
lst=son[p][c]; sz[lst]++;
} int main(){
freopen("bzoj2160.in","r",stdin);
freopen("bzoj2160.out","w",stdout);
scanf("%d%lld%s",&n,&K,s+);
len[]=-; fail[]=fail[]=;
rep(i,,n) ext(s[i]-'a',i,s);
for (int i=nd; i; i--) sz[fail[i]]+=sz[i];
rep(i,,nd) c[i-]=(P){len[i],sz[i]};
sort(c+,c+nd);
rep(i,,nd-){
if (!(c[i].l&)) continue;
ll t=min(K,(ll)c[i].c); ans=1ll*ans*ksm(c[i].l,t)%mod; K-=t;
if (!K) break;
}
printf("%d\n",K?-:ans);
return ;
}

BZOJ2160

[BZOJ3676][APIO2014]回文串

显然建出回文树后求出每个点的出现次数与长度即可。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
char s[N];
ll ans;
int n,lst,nd=,len[N],fail[N],sz[N],son[N][]; void ext(int c,int n,char s[]){
int p=lst;
while (s[n-len[p]-]!=s[n]) p=fail[p];
if (!son[p][c]){
int np=++nd,q=fail[p];
while (s[n-len[q]-]!=s[n]) q=fail[q];
len[np]=len[p]+; fail[np]=son[q][c]; son[p][c]=np;
}
sz[lst=son[p][c]]++;
} int main(){
freopen("bzoj3676.in","r",stdin);
freopen("bzoj3676.out","w",stdout);
scanf("%s",s+); n=strlen(s+);
fail[]=fail[]=; len[]=-; s[]=-;
rep(i,,n) ext(s[i]-'a',i,s);
for (int i=nd; i; i--) sz[fail[i]]+=sz[i];
rep(i,,nd) ans=max(ans,1ll*sz[i]*len[i]);
printf("%lld\n",ans);
return ;
}

BZOJ3676

[CF17E]Palisection

正难则反,所有回文串对数减去不相交对数。以某个位置结尾的回文子串个数等于它在回文树上代表的节点的深度,后缀和优化一下即可。

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
typedef long long ll;
using namespace std; const int N=,mod=;
char s[N];
int n,cnt,ans,nd,lst,p1[N],p2[N],dep[N],to[N],nxt[N],val[N],h[N],fail[N],len[N]; void add(int u,int v,int w){ to[++cnt]=v; nxt[cnt]=h[u]; val[cnt]=w; h[u]=cnt; } void init(){
rep(i,,nd) h[i]=fail[i]=len[i]=dep[i]=;
cnt=lst=; nd=; len[]=-; fail[]=fail[]=;
} int son(int x,int c){ For(i,x) if (val[i]==c) return k=to[i]; return ; } void ext(int c,int n,char s[]){
int p=lst;
while (s[n-len[p]-]!=s[n]) p=fail[p];
if (!son(p,c)){
int np=++nd,q=fail[p];
while (s[n-len[q]-]!=s[n]) q=fail[q];
len[np]=len[p]+; fail[np]=son(q,c); dep[np]=dep[fail[np]]+; add(p,np,c);
}
lst=son(p,c);
} int main(){
freopen("cf17e.in","r",stdin);
freopen("cf17e.out","w",stdout);
scanf("%d%s",&n,s+); init();
rep(i,,n) ext(s[i]-'a',i,s),p1[i]=dep[lst],ans=(ans+p1[i])%mod;
ans=1ll*ans*(ans-)/%mod; reverse(s+,s+n+); init();
rep(i,,n) ext(s[i]-'a',i,s),p2[n-i+]=dep[lst];
for (int i=n; i; i--) p2[i]=(p2[i]+p2[i+])%mod;
rep(i,,n) ans=(ans-1ll*p1[i]*p2[i+]%mod+mod)%mod;
printf("%d\n",ans);
return ;
}

CF17E

[Aizu2292]Common Palindromes

给定S,T,询问有多少(l1,r1,l2,r2)使得S[l1,r1]回文且S[l1,r1]=T[l2,r2]。

显然对S建出回文自动机然后T在上面跑,记录每个S中回文串的出现次数以及T中有多少个子串与此串匹配。注意初始x=1(可以认为回文树的根是1),且匹配是不仅要看此节点是否有对应子节点,也要看s[i-len[x]-1]是否等于s[i]。

 #include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
char s[N];
ll ans;
int n,lst,nd=,f[N],son[N][],fail[N],len[N],sz[N]; void ext(int c,int n,char s[]){
int p=lst;
while (s[n-len[p]-]!=s[n]) p=fail[p];
if (!son[p][c]){
int np=++nd,q=fail[p];
while (s[n-len[q]-]!=s[n]) q=fail[q];
len[np]=len[p]+; fail[np]=son[q][c]; son[p][c]=np;
}
sz[lst=son[p][c]]++;
} int main(){
freopen("Aizu2292.in","r",stdin);
freopen("Aizu2292.out","w",stdout);
len[]=-; fail[]=fail[]=;
scanf("%s",s+); n=strlen(s+);
rep(i,,n) ext(s[i]-'A',i,s);
scanf("%s",s+); n=strlen(s+); int x=;
rep(i,,n){
int c=s[i]-'A';
while (x!= && (!son[x][c] || s[i]!=s[i-len[x]-])) x=fail[x];
if (son[x][c] && s[i]==s[i-len[x]-]) x=son[x][c],f[x]++;
}
for (int i=nd; i; i--) f[fail[i]]+=f[i],sz[fail[i]]+=sz[i];
rep(i,,nd) ans+=1ll*f[i]*sz[i];
printf("%lld\n",ans);
return ;
}

Aizu2292

[BZOJ2342][SHOI2011]双倍回文

就是求后半段也为回文串的回文串个数,在fail树上DFS并维护每个长度的回文串个数即可。

或者考虑求half[i]表示节点i的最深祖先满足len[half[i]]<=len[i]/2,这个同样可以在建树的时候求得。

 #include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; const int N=;
char s[N];
int n,ans,lst,nd=,fail[N],son[N][],len[N],half[N]; void ext(int c,int n,char s[]){
int p=lst;
while (s[n-len[p]-]!=s[n]) p=fail[p];
if (!son[p][c]){
int np=++nd,q=fail[p];
while (s[n-len[q]-]!=s[n]) q=fail[q];
len[np]=len[p]+; fail[np]=son[q][c]; son[p][c]=np;
if (len[np]==) half[np]=;
else{
int pos=half[p];
while (s[n-len[pos]-]!=s[n] || (len[pos]+)*>len[np]) pos=fail[pos];
half[np]=son[pos][c];
}
}
lst=son[p][c];
} int main(){
freopen("bzoj2342.in","r",stdin);
freopen("bzoj2342.out","w",stdout);
scanf("%d%s",&n,s+); len[]=-; fail[]=fail[]=;
rep(i,,n) ext(s[i]-'a',i,s);
rep(i,,nd) if (len[half[i]]*==len[i] && len[i]%==) ans=max(ans,len[i]);
printf("%d\n",ans);
return ;
}

BZOJ2342

回文树/回文自动机(PAM)学习笔记的更多相关文章

  1. [模板] 回文树/回文自动机 && BZOJ3676:[Apio2014]回文串

    回文树/回文自动机 放链接: 回文树或者回文自动机,及相关例题 - F.W.Nietzsche - 博客园 状态数的线性证明 并没有看懂上面的证明,所以自己脑补了一个... 引理: 每一个回文串都是字 ...

  2. 回文树(回文自动机) - URAL 1960 Palindromes and Super Abilities

     Palindromes and Super Abilities Problem's Link: http://acm.timus.ru/problem.aspx?space=1&num=19 ...

  3. 回文树(回文自动机PAM)小结

    回文树学习博客:lwfcgz    poursoul 边写边更新,大概会把回文树总结在一个博客里吧... 回文树的功能 假设我们有一个串S,S下标从0开始,则回文树能做到如下几点: 1.求串S前缀0~ ...

  4. 回文树(回文自动机) - BZOJ 3676 回文串

    BZOJ 3676 回文串 Problem's Link: http://www.lydsy.com/JudgeOnline/problem.php?id=3676 Mean: 略 analyse: ...

  5. BZOJ 3676: [Apio2014]回文串 回文树 回文自动机

    http://www.lydsy.com/JudgeOnline/problem.php?id=3676 另一种更简单更快常数更小的写法,很神奇……背板子. #include<iostream& ...

  6. JavaScript权威设计--JavaScript脚本化文档Document与CSS(简要学习笔记十五)

    1.Document与Element和TEXT是Node的子类. Document:树形的根部节点 Element:HTML元素的节点 TEXT:文本节点   >>HtmlElement与 ...

  7. PAM学习笔记

    想了想 还是要先把字符串的东西先都学完告一段落了再说 时间不多了 加油~. PAM 回文自动机 比SAM简单到不知道哪里去了. 回文自动机和其他自动机一样有字符集 有状态 有转移. 一个字符串的回文自 ...

  8. 「AC自动机」学习笔记

    AC自动机(Aho-Corasick Automaton),虽然不能够帮你自动AC,但是真的还是非常神奇的一个数据结构.AC自动机用来处理多模式串匹配问题,可以看做是KMP(单模式串匹配问题)的升级版 ...

  9. 设备树(device tree)学习笔记

    作者信息 作者:彭东林 邮箱:pengdonglin137@163.com 1.反编译设备树 在设备树学习的时候,如果可以看到最终生成的设备树的内容,对于我们学习设备树以及分析问题有很大帮助.这里我们 ...

随机推荐

  1. kernel 获取ntoskrnl.exe基址

    标题: kernel shellcode之寻找ntoskrnl.exe基址 http://scz.617.cn:8/windows/201704171416.txt 以64-bits为例,这是Eter ...

  2. 【AtCoder】 ARC 100

    link C-Linear Approximation 给出\(N\)个数\(A_1,A_2,...,A_N\) ,求一个数\(d\),最小化\(\sum_{i=1}^N|A_i-(d+i)|\) 把 ...

  3. 如何新建WebAPI,生成注释,TestAPI的项目

    一.新建WebAPI的项目 1. 在Web下,ASP.NET Web 应用程序,点击确定 2. 点击确定 3. 如图所示, 新建Controller 4 . 运行项目 二.注释 1. 在生成中,勾选x ...

  4. redis渐进式rehash机制

    在Redis中,键值对(Key-Value Pair)存储方式是由字典(Dict)保存的,而字典底层是通过哈希表来实现的.通过哈希表中的节点保存字典中的键值对.我们知道当HashMap中由于Hash冲 ...

  5. ConcurrentHashMap 无锁读

    ConcurrentHashMap 可以做到无锁读,而写使用分段锁机制,把整个哈希表切分成段segment(默认为16段),每段有一个锁,最多可以同时有16个写线程.而读不受限制. 下文转自http: ...

  6. Spring Cloud-Eureka 服务注册中心

    Eureka 是 Netflix 开发的,一个基于 REST 服务的,服务注册与发现的组件 它主要包括两个组件:Eureka Server 和 Eureka Client Eureka Client: ...

  7. SonarQube代码评审工具简介

    SonarQube是一个代码评审工具,可以完成对多种类型代码的扫描,并生成报告.本文是一个简单的扫描Java代码的使用说明. 该工具主要分为两个部分: 服务端:用来保存和展示扫描结果. 客户端:或者说 ...

  8. jquery ajax Uncaught TypeError :Illegal invocation 报错

    使用jquery ajax异步提交的时候报Uncaught TypeError :Illegal invocation错误,报错如图: 基本上,导致这个错误的原因一般有以下两点: 1.请求类型有误,如 ...

  9. SAP翔子_ABAP_DEMO篇索引

    序号 描述 SAP翔子_ABAP_DEMO篇1 ABAP DEMO篇1 单层反查BOM SAP翔子_ABAP_DEMO篇2 ABAP DEMO篇2 删除工艺路线 SAP翔子_ABAP_DEMO篇3 A ...

  10. EasyDSS高性能RTMP、HLS(m3u8)、HTTP-FLV、RTSP流媒体服务器启用https服务申请免费证书

    背景分析 目前想在 web 上使用 HTTPS 的话, 你需要获得一个证书文件, 该证书由一个受浏览器信任的公司所签署. 一旦你获得了它, 你就在你的 web 服务器上指定其所在的位置, 以及与你关联 ...