The Problem to Slow Down You(Palindromic Tree)
题目链接: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)的更多相关文章
- 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 ...
- Palindromic Tree 回文自动机-回文树 例题+讲解
回文树,也叫回文自动机,是2014年被西伯利亚民族发明的,其功能如下: 1.求前缀字符串中的本质不同的回文串种类 2.求每个本质不同回文串的个数 3.以下标i为结尾的回文串个数/种类 4.每个本质不同 ...
- 回文自动机 + 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 ...
- 回文树 Palindromic Tree
回文树 Palindromic Tree 嗯..回文树是个什么东西呢. 回文树(或者说是回文自动机)每个节点代表一个本质不同的回文串. 首先它类似字典树,每个节点有SIGMA个儿子,表示对应的字母. ...
- 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 ...
- The Problem to Slow Down You
The Problem to Slow Down You 输入:t个测试样例,每个样例输入两个字符串 输出:这两对字符串的回文串可以组成多少对本质不同的回文串 题意:给你两个字符串,然后问你这两字符串 ...
- UVAlive 7041 The Problem to Slow Down You(回文树)
题目链接: https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show ...
- HDU6430 Problem E. TeaTree【dsu on tree】
Problem E. TeaTree Problem Description Recently, TeaTree acquire new knoledge gcd (Greatest Common D ...
- UVALive - 7041 The Problem to Slow Down You (回文树)
https://vjudge.net/problem/UVALive-7041 题意 给出两个仅包含小写字符的字符串 A 和 B : 求:对于 A 中的每个回文子串,B 中和该子串相同的子串个数的总和 ...
随机推荐
- Dapper基础增删查改、事务和存储过程
1.前言 Dapper是一个轻量级的orm框架,上手也非常的简单,它可以实体映射,所以先准备实体如下: public class Couser { public int id { get; set; ...
- [记读书笔]python3.5实现socket通讯(UDP)
UDP连接: 无连接,从一个端向另一端发送独立的数据分组 使用UDP连接的客户-服务器程序: UDPServer.py import socket serverPort = 50009 serverS ...
- 安装完最小化 RHEL/CentOS 7 后需要做的 30 件事情(二)
本文导航 -7. 安装 PHP0 -8. 安装 MariaDB 数据库 -9. 安装和配置 SSH 服务器 -10. 安装 GCC (GNU 编译器集) -11. 安装 Java 7. 安装 PHP ...
- Eclipse 修改字符集---Eclipse教程第02课
默认情况下 Eclipse 字符集为 GBK,但现在很多项目采用的是 UTF-8,这是我们就需要设置我们的 Eclipse 开发环境字符集为 UTF-8, 设置步骤如下: 在菜单栏选择 Window ...
- 剑指Offer - 九度1513 - 二进制中1的个数
剑指Offer - 九度1513 - 二进制中1的个数2013-11-29 23:35 题目描述: 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 输入: 输入可能包含多个测试样例. ...
- Lazarus教程 中文版后续给出
市面上有介绍Delphi的书籍(近来Delphi的书也是越来越少了),但没有一本系统的介绍Lazarus的书,这本书是网上的仅有的一本Lazarus教程,目前全部是英文,不过我已经着手开始翻译,争取尽 ...
- Vbs 测试程序一
转载请注明出处 有点小恶意哦!慎重测试 'This procedure is written in SeChaos, only for entertainment, not malicious com ...
- Asp.Net中Response.Cookies.Remove 无法删除COOKIE的问题解决方法
登陆功能经常需要使用Cookie来存储登陆信息,可是在开发过程中,经常发现cookie无法删除的问题.删除的代码无非就是找到Cookie并删除掉. 但是会发现 Response.Cookies.Rem ...
- 更改maven本地仓库地址
1.进入maven安装conf文件中,编辑settings.xml文件,新增图中的圈出的内容(我想要存放的地址是D:\HMY\m2\repository) 2.复制settings.xml文件至D:\ ...
- 孤荷凌寒自学python第六十三天学习mongoDB的基本操作并进行简单封装2
孤荷凌寒自学python第六十三天学习mongoDB的基本操作并进行简单封装2 (完整学习过程屏幕记录视频地址在文末) 今天是学习mongoDB数据库的第九天. 今天继续学习mongoDB的简单操作, ...