题目链接:http://codeforces.com/gym/100548

今天晚上突然有了些兴致去学习一下数据结构,然后就各种无意中看到了Palindrome Tree的数据结构,据说是2014年新出的数据结构,也让我回想起了西安打铁时候的经历。这道题的题意其实是比较清晰的,给你两个长度200000的字符串,求它们有多少对回文子串。处理字符串有许多常用的工具,像后缀数组,后缀自动机,AC自动机,KMP,但是这些数据结构在针对回文串的处理上都不是特别强,当然稍微处理一下我们还是可以很好的利用好上面这些数据结构处理一些回文的问题。另外一个关于回文的问题就是求某个串的最长公共子串了,一个O(n)的Manachar算法算是解决了这个问题。Palindrome Tree似乎就是专门为了填补对于处理回文串的空白而产生的,而且最神的是,当你有了前面的一些数据结构的基础,再去看这个数据结构的整个结构,算法的流程,你会觉得很清晰,非常容易理解,当然这也是多亏了下面的博客给了我代码的模板以及对数据结构的一些理解。

http://blog.csdn.net/u013368721/article/details/42100363

http://blog.csdn.net/u013368721/article/details/42104207

下面是谈谈自己对上面博客阐述内容的一些个人的理解

首先是对数据结构的成员作一个简单的描述:
nxt[i][ch]   节点i在接收了字符ch之后所指向的节点
fail[i]      以节点i所指代的回文串的末尾字符结束的上一个回文串 fail[ ababa ] = aba  fail[aba] = a
cnt[i]       节点i所表示的回文串的个数
num[i]       以节点i所表示的回文串的末尾字符结束的回文串的个数
len[i]       节点i所表示的回文串的长度
S[i]         第i个字符
last         新添加的字符所产生的最长回文串的节点
p            节点的总的个数
n            当前字符串的长度

其中个人觉得比较难理解的是fail[i]. 假如i节点指代的回文串是"ababa"的话,那么fail[i]就是指以这个回文串的末尾字符结尾(就是'a')的上一个最长的回文串,也即是aba,再上一个自然就是a了。相似的是KMP里的失配指针,或者是在后缀自动机里上一个可以接收的后缀。

接下来就是整个算法的描述了

1.一开始初始化0号节点和1号节点,分别表示长度为偶数的节点(即空串),和1号节点(长度为奇数的空串),显然空串的长度是0,而这样的奇数串是不存在的,len[0]=0,len[1]=-1. 这个len[1]设成-1有着其独道的好处,具体的一些好处可以参看上面的博客。

2.接下来考虑添加1个新字符ch,首先要注意的是getFail()函数的作用,while (S[n - len[x] - 1] != S[n]) x = fail[x]; 的实际含义是找到一个能够接收当前新字符的回文串的编号,  对原串"abacaba"新加1个c,S[n-len[x]-1]=a != c, 所以回到上一个回文串节点,即表示aba的节点,这个时候S[n-len[x]-1]=c,满足题意,于是乎我们就找到了一个可以接收这个新状态的节点cur

3.接下来我们就可以更新nxt[cur][ch],当这个节点不存在的时候,我们需要新建这样的节点,长度是len[cur]+2,然后我们还需要知道新建的这个节点now的fail值,fail[now]=nxt[getFail(fail[cur])][ch],即当前新建立的节点的上一个回文串,getFail(fail[cur])返回的是以fail[cur]对应字符串结尾的可以接收ch的回文串。所以可以这么理解 now=nxt[getFail(cur)][ch],fail[now]=nxt[getFail(fail[cur])][ch].

4.然后更新一下新节点的num值,以及cnt值,注意的是这里的cnt值并不代表该节点对应的回文子串出现的总次数,正如后缀自动机里记数的时候cnt也不能表示这个字符串出现的总次数,最后需要有一个结果回加的过程,即是count的过程。

最后整个代码是有着比较清晰的结构的,下面对于这题的代码就是用了上面博客提供的模板。

对于原本的题目,需要做的就是在偶串中心节点0,和奇串中心节点1,不停地往两边塞字符就好了,当两边的状态都存在的时候继续往下dfs,最后统计一下结果即可。

这个数据结构维护的信息其实可以用作很多特别的用途。

1.直接dfs(0),dfs(1)我们可以递归的打印出所有回文串以及各自出现的次数

2.新添加1个字符是否能产生新的回文串,即可以在线的得出S的前缀i包含的不同的回文子串的个数。

3.num[i]可以得到对每个新增的字符为结尾的回文串的个数,以及len[i]求出以这个字符结尾的最长回文串长度

总之个人觉得这个数据结构可以做绝大多数的回文串的题目了,而且这个数据结构非常的elegent.字符串长度为n,字符集大小为m,则空间复杂度是O(nm),至于时间复杂度的话,我也不知道是怎么证明的,但是据说是O(nlogm),可以说对于字符集经常固定的题目来说就是一个O(n)的算法了。

#pragma warning(disable:4996)
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <cstdio>
#include <algorithm>
using namespace std; #define MAXN 210000
#define ll long long struct PalindromeTree
{
/*static variables*/
const static int maxn = 210000;
const static int ch = 26;
/*
* nxt[i][ch] 节点i在接收了字符ch之后所指向的节点
* fail[i] 以节点i所指代的回文串的末尾字符结束的上一个回文串 fail[ ababa ] = aba fail[aba] = a
* cnt[i] 节点i所表示的回文串的个数
* num[i] 以节点i所表示的回文串的末尾字符结束的回文串的个数
* len[i] 节点i所表示的回文串的长度
* S[i] 第i个字符
* last 新添加的字符所产生的最长回文串的节点
* p 节点的总的个数
* n 当前字符串的长度
*/
int nxt[maxn][ch];
int fail[maxn];
int cnt[maxn];
int num[maxn];
int len[maxn];
int S[maxn];
int last;
int n;
int p; int newnode(int length){
memset(nxt[p], 0, sizeof(nxt[p]));
cnt[p] = num[p] = 0;
len[p] = length;
return p++;
} void init(){
p = 0;
newnode(0);
newnode(-1);
last = 0;
n = 0;
S[n] = -1;
fail[0] = 1;
} int getFail(int x){
while (S[n - len[x] - 1] != S[n]) x = fail[x];
return x;
} void add(int c) {
c -= 'a';
S[++n] = c;
int cur = getFail(last);
if (!nxt[cur][c]) {
int now = newnode(len[cur] + 2);
fail[now] = nxt[getFail(fail[cur])][c];
nxt[cur][c] = now;
num[now] = num[fail[now]] + 1;
}
last = nxt[cur][c];
cnt[last] ++;
} void count(){
for (int i = p - 1; i >= 0; --i){
cnt[fail[i]] += cnt[i];
}
}
}; PalindromeTree treex, treey;
char buf1[MAXN], buf2[MAXN]; ll ans; void dfs(int u, int v)
{
for (int i = 0; i < treex.ch; ++i){
int x = treex.nxt[u][i];
int y = treey.nxt[v][i];
if (x&&y){
ans += (ll)treex.cnt[x] * treey.cnt[y];
dfs(x, y);
}
}
} int main()
{
int T; cin >> T; int ca = 0;
while (T--)
{
treex.init();
treey.init();
scanf("%s%s", buf1, buf2);
int len1 = strlen(buf1), len2 = strlen(buf2);
for (int i = 0; i < len1; ++i) {
treex.add(buf1[i]);
}
for (int i = 0; i < len2; ++i){
treey.add(buf2[i]);
}
treex.count();
treey.count();
ans = 0;
dfs(0, 0);
dfs(1, 1);
printf("Case #%d: %I64d\n", ++ca, ans);
}
return 0;
}

The Problem to Slow Down You(Palindromic Tree)的更多相关文章

  1. 2014-2015 ACM-ICPC, Asia Xian Regional Contest G The Problem to Slow Down You 回文树

    The Problem to Slow Down You Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjud ...

  2. Palindromic Tree 回文自动机-回文树 例题+讲解

    回文树,也叫回文自动机,是2014年被西伯利亚民族发明的,其功能如下: 1.求前缀字符串中的本质不同的回文串种类 2.求每个本质不同回文串的个数 3.以下标i为结尾的回文串个数/种类 4.每个本质不同 ...

  3. 回文自动机 + DFS --- The 2014 ACM-ICPC Asia Xi’an Regional Contest Problem G.The Problem to Slow Down You

    The Problem to Slow Down You Problem's Link: http://acm.hust.edu.cn/vjudge/problem/viewProblem.actio ...

  4. 回文树 Palindromic Tree

    回文树 Palindromic Tree 嗯..回文树是个什么东西呢. 回文树(或者说是回文自动机)每个节点代表一个本质不同的回文串. 首先它类似字典树,每个节点有SIGMA个儿子,表示对应的字母. ...

  5. CodeForcesGym 100548G The Problem to Slow Down You

    The Problem to Slow Down You Time Limit: 20000ms Memory Limit: 524288KB This problem will be judged ...

  6. The Problem to Slow Down You

    The Problem to Slow Down You 输入:t个测试样例,每个样例输入两个字符串 输出:这两对字符串的回文串可以组成多少对本质不同的回文串 题意:给你两个字符串,然后问你这两字符串 ...

  7. UVAlive 7041 The Problem to Slow Down You(回文树)

    题目链接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show ...

  8. HDU6430 Problem E. TeaTree【dsu on tree】

    Problem E. TeaTree Problem Description Recently, TeaTree acquire new knoledge gcd (Greatest Common D ...

  9. UVALive - 7041 The Problem to Slow Down You (回文树)

    https://vjudge.net/problem/UVALive-7041 题意 给出两个仅包含小写字符的字符串 A 和 B : 求:对于 A 中的每个回文子串,B 中和该子串相同的子串个数的总和 ...

随机推荐

  1. WPF点击不同界面上的按钮实现界面切换

    原文:WPF点击不同界面上的按钮实现界面切换 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/qq_29844879/article/details/ ...

  2. 第四模块:网络编程进阶&数据库开发 口述

    进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 子进程死了之后 ,父进程关闭的时候要清理掉子进程的僵尸进程(收尸),孤儿进程是指父进程先死掉了的,交给init管理. join() 等待子进 ...

  3. 1、python 循环控制

     案例1: lucky_num = 19 input_num = int(input("Input the guess number:")) if input_num == luc ...

  4. ASP.NET Core 2.1 源码学习之 Options[1]:Configure 【转】

    原文链接:https://www.cnblogs.com/RainingNight/p/strongly-typed-options-configure-in-asp-net-core.html 配置 ...

  5. Windows后续处理工作

    1.远程桌面开启,应预先开启windows防火墙,并放行“远程桌面”(TCP 3389)端口,防止用户自行开启防火墙时操作错误. 2.防火墙高级安全-需放行ICMP 3.补丁更新,更新完重启 4.本地 ...

  6. Megacli查看Dell服务器Raid状态

    通常我们使用的DELL/HP/IBM三家的机架式PC级服务器阵列卡是从LSI的卡OEM出来的,DELL和IBM两家的阵列卡原生程度较高,没有做太多封装,可以用原厂提供的阵列卡管理工具进行监控:而HP的 ...

  7. 聊聊、SpringBoot 上传文件大小

    #2.0#spring.servlet.multipart.max-file-size=10Mb#spring.servlet.multipart.max-request-size=10Mb #1.3 ...

  8. 将MSHFlexGrid1中记录导出为Excel

    1.添加引用Microsoft Excel 14.0 Object Library 2.编写代码部分 Private Sub Output_Click() Dim i As Integer '定义变量 ...

  9. InnoDB 存储引擎的线程与内存池

    InnoDB 存储引擎的线程与内存池   InnoDB体系结构如下: 后台线程: 1.后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据: 2.另外,将以修改的数据文件刷 ...

  10. 【bzoj4756】[Usaco2017 Jan]Promotion Counting 离散化+树状数组

    原文地址:http://www.cnblogs.com/GXZlegend/p/6832263.html 题目描述 The cows have once again tried to form a s ...