[JZOJ3402] 【GDOI2014模拟】Pty的字符串
题目
给你一棵每条边从父亲指向儿子的树,每条边上面有一个字母。
从树上的任意一点出发,走出的路径就是对应一个子串。
(这不是\(Trie\),因为每个父亲可能会连出字母相同的边)
再给你一个字符串\(S\),让你求\(S\)的子串和树上路径的对应个数。
思考历程
一开始以为路径是从根节点出发,于是我就想,这难道不是一个AC自动机的裸题吗?
啪啪啪地就把AC自动机打了上去……
然后发现样例过不去……
于是终于理解完题目大意,开始死磕。
但是一直都没有放弃AC自动机的做法。
一波乱搞后,我终于爆0了。
正解
后来才意识到其实这题是后缀自动机。
于是就开始自己刚后缀自动机的做法了……
大体思路是处理出树上的每个子串的出现次数,然后跟\(S\)的子串进行匹配。
先想想前面的这个问题。
首先,由于这是一棵树,所以就对整棵树建广义后缀自动机。
至于这是什么东西……我想就不该在这里赘述了。
然后有个点\(i\)从树的根部往下跑。跑的时候将\(root\)到\(i\)路径所组成的字符串的所有后缀统计进来。
\(i\)在跑的时候也有个点\(t\)在后缀自动机上跑。对于每对\((i,t)\),就将\(t\)的\(fail\)链上的所有统计次数\(num+1\)。
显然,由于节点所表示的子串的\(right\)集合相同,所以\(right\)集合大小相同,所以它们的出现次数也相同。所以\(num\)表示的是这个节点的所有子串各自出现的次数。
在实现的时候,可以先在\(t\)点打个标记,跑完之后\(fail\)树上标记上传就好了(如果你喜欢就打树链剖分吧~~(手动滑稽))。
这样我们就处理出树上的每个子串的出现次数了,接下来是匹配的问题。
也是枚举个\(i\)表示子串的右边界,也有个\(t\)在后缀自动机上面跳。
设\(l\)为以\(i\)为结尾的在树中出现过的最长的子串长度。
显然,\(t\)在跳的过程中保证了\(t_{minlen}\leq l\leq t_{maxlen}\)。
由于子串\(S_{i-l+1..i}\)的所有后缀都要记录进答案中,而\(t\)的\(fail\)链上所有节点表示的子串都是它的后缀,所以就将\(fail\)链上的出现次数全部加上(即为累加\(num*(maxlen-minlen+1)\))。具体实现的时候可以用个前缀和将祖先的出现次数全部存下来,记作\(sum\)。
我们计算的是长度在\([1,l]\)的后缀,可以拆成\([1,t_{minlen}-1]\)和\([t_{minlen},l]\)这两段,前者的答案是\(t_{fail_{sum}}\),后者的答案为\(t_{num}(l-t_{fail_{maxlen}})\),加起来就行了。
然后问题就变成了求\(l\)的具体值。
这实际上很简单,只不过我之前想了很久,甚至恨不得再打一个AC自动机来求。
在一开始的时候\(l=0\)。后来,当\(t\)可以往前走的时候,\(l\)的值也相应地\(+1\)。当\(t\)要跳\(fail\)的时候,就将\(l\)和\(t_{fail_{maxlen}}\)取个最小值。具体原因不再赘述。
于是这道题就没了……
代码
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 800010
#define LEN 8000010
int n;
struct EDGE{
int to,c;
EDGE *las;
} e[N];
int ne;
EDGE *last[N];
struct Node{
Node *c[3],*fail;
int len;
int num;
long long sum;
} d[N*2];
int cnt;
Node *S,*T;
Node *mp[N];
inline void insert(int ch){
if (T->c[ch] && T->c[ch]->len==T->len+1){
T=T->c[ch];
return;
}
Node *nw=&d[++cnt],*p,*q;
nw->len=T->len+1;
for (p=T;p && !p->c[ch];p=p->fail)
p->c[ch]=nw;
if (!p)
nw->fail=S;
else{
q=p->c[ch];
if (p->len+1==q->len)
nw->fail=q;
else{
Node *clone=&d[++cnt];
memcpy(clone,q,sizeof(Node));
clone->len=p->len+1;
for (;p && p->c[ch]==q;p=p->fail)
p->c[ch]=clone;
nw->fail=q->fail=clone;
}
}
T=nw;
}
inline void build(){
static int q[N];
T=S=&d[++cnt];
int head=0,tail=1;
q[1]=1;
mp[1]=S;
do{
int x=q[++head];
for (EDGE *ei=last[x];ei;ei=ei->las){
T=mp[x];
insert(ei->c);
mp[ei->to]=T;
q[++tail]=ei->to;
}
}
while (head!=tail);
}
inline void hang(){
static pair<int,Node*> q[N];
int head=0,tail=1;
q[1]={1,S};
do{
++head;
int x=q[head].first;
Node *ht=q[head].second;
for (EDGE *ei=last[x];ei;ei=ei->las){
Node *tt=ht;
while (tt!=S && !tt->c[ei->c])
tt=tt->fail;
if (tt->c[ei->c])
tt=tt->c[ei->c];
tt->num++;
q[++tail]={ei->to,tt};
}
}
while (head!=tail);
}
Node *q[N];
bool vis[N];
inline void get_sum(){
int head=0,tail=1;
q[1]=S;
vis[1]=1;
do{
Node *x=q[++head];
for (int i=0;i<3;++i)
if (x->c[i] && !vis[x->c[i]-d]){
vis[x->c[i]-d]=1;
q[++tail]=x->c[i];
}
}
while (head!=tail);
for (int i=tail;i>=2;--i){
Node *x=q[i];
x->fail->num+=x->num;
}
for (int i=2;i<=tail;++i){
Node *x=q[i];
x->sum=x->fail->sum+1ll*x->num*(x->len-x->fail->len);
}
}
char s[LEN];
int main(){
scanf("%d",&n);
for (int i=2;i<=n;++i){
int fa;
char c[2];
scanf("%d%s",&fa,c);
e[ne]={i,*c-'a',last[fa]};
last[fa]=e+ne++;
}
build(),hang(),get_sum();
Node *t=S;
scanf("%s",s);
long long ans=0;
int len=0;
for (char *ch=s;*ch;++ch){
while (t!=S && !t->c[*ch-'a']){
t=t->fail;
len=min(len,t->len);
}
if (t->c[*ch-'a']){
t=t->c[*ch-'a'];
len++;
}
if (t!=S)
ans+=t->fail->sum+1ll*t->num*(len-t->fail->len);
}
printf("%lld\n",ans);
return 0;
}
总结
这也算是一道后缀自动机的模板题吧……
看来还是不够熟练啊……
[JZOJ3402] 【GDOI2014模拟】Pty的字符串的更多相关文章
- GDOI2014模拟pty爬山(mountain)
pty爬山(mountain) 在Pty学校附近,有一座名之为岳之麓的高山.Pty很喜欢和(哔--)一起爬山.山的平面模型如下:山由一个顶点集:A1,A2-An给定,保证Ai的x单调递增.我们将Ai和 ...
- 【GDOI2014模拟】JZOJ2020年8月14日提高组 服务器
[GDOI2014模拟]JZOJ2020年8月14日提高组 服务器 题目 Time and Memory Limits Description 我们需要将一个文件复制到n个服务器上,这些服务器的编号为 ...
- 【GDOI2014模拟】JZOJ2020年8月14日T2 网格
[GDOI2014模拟]JZOJ2020年8月14日T2 网格 题目 Time and Memory Limits Description 某城市的街道呈网格状,左下角坐标为A(0, 0),右上角坐标 ...
- bzoj3756: Pty的字符串
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...
- 计蒜客蓝桥杯模拟赛 后缀字符串:STL_map+贪心
问题描述 一天蒜头君得到 n 个字符串 si,每个字符串的长度都不超过 10. 蒜头君在想,在这 n 个字符串中,以 si 为后缀的字符串有多少个呢? 输入格式 第一行输入一个整数 n. 接下来 ...
- 洛谷 P3695 CYaRon!语 题解 【模拟】【字符串】
大模拟好啊! 万一远古计算机让我写个解释器还真是得爆零了呢. 题目背景 「千歌です」(我是千歌).「曜です」(我是曜).「ルビィです」(我是露比).「3人合わせて.We are CYaRon! よろし ...
- Pty的字符串(string)
题目描述 在神秘的东方有一棵奇葩的树,它有一个固定的根节点(编号为1).树的每条边上都是一个字符,字符为a,b,c中的一个,你可以从树上的任意一个点出发,然后沿着远离根的边往下行走,在任意一个节点停止 ...
- Hidden (NOIP模拟赛)(字符串模拟QAQ)
原题传送门 神奇的题目诶 原来以为字符串比较一定要O(NlogN) 结果发现可以均摊O(N) 首先我们来讲一讲原理 我们有3个指针i,j,k i=0,j=1,k=0 一开始我们不断对k+1直到找到ch ...
- 常州模拟赛d4t3 字符串划分
题目描述 给你一串由小写字母组成的字符串,希望你把它划分成一些小段,使得每一小段字符串中的字母 都不相同,并且希望分的段数尽量少. 然后,把这些小段按字典序排序后输出,中间由一个空格分隔. 例如:字符 ...
随机推荐
- 5-MySQL-Ubuntu-操作数据库的基本操作语句
注意: (1)每一条sql语句都是以分号(;)结尾! (2)数据库的默认charset不支持中文,所以每次在创建数据库的时候要指定字符集charset=utf8; (一) 查看当前时间: select ...
- VMware1设备与主机共享网络的问题
问题的提出: 最近需要用到VMware1设备来配置网络,顺便将VMware1设备与主机进行共享网络,这样master就能直接访问网络了,但是原本以为直接在wlan设备上选择网络共享就行了,但是却没法收 ...
- wkhtmltopdf linux下html转pdf
https://blog.csdn.net/wujunlei1595848/article/details/91129197 https://github.com/wkhtmltopdf/wkhtml ...
- 用python 编写redis 暴力破解密码的程序
本文摘自http://blog.knownsec.com/2015/11/analysis-of-redis-unauthorized-of-expolit/ import redisimport l ...
- leetcood学习笔记-107-二叉树的层次遍历二
题目描述: 方法一: class Solution(object): def levelOrderBottom(self, root): """ :type root: ...
- Spring Data之Example<>
简单CRUD之Example动态查询 简单介绍 (部分口水话,部分来自网络,代码永远自产) 使用过Spring全家桶的各位大佬应该都知道,Spring Data这个是Spring对持久层框架的封装,比 ...
- 50 ubuntu下pcl编译以及用 VSCode配置pcl / opencv开发环境
0 引言 最近在VSCode下搞开发,于是将pcl库迁移到这个环境下,用来跑一些依赖pcl的开源的代码以及自己做一些快速开发等. 1 pcl编译 主要参考了这篇博客,链接如下. https://blo ...
- sqoop一些常用命令及参数
常用命令列举 这里给大家列出来了一部分Sqoop操作时的常用参数,以供参考,需要深入学习的可以参看对应类的源代码. 序号 命令 类 说明 1 import ImportTool 将数据导入到集群 2 ...
- NX二次开发-char*转换成CString,多字节转换成Unicode使用方法
//定义一个结构体记录 struct group { CString text; //定义一个CString std::vector<tag_t> boudaries; std::vect ...
- Linux下同一目录内文件和目录为什么不能同名?
问题描述: 如果事先有叫‘A’的文件夹存在,则不允许建立叫‘A’的文件: 同理若先有叫‘b’的文件存在,一样不允许建立叫‘b’的文件夹. 原因很简单,因为Linux下一切都是文件,一个目录归根到底还是 ...