[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 字符串划分
题目描述 给你一串由小写字母组成的字符串,希望你把它划分成一些小段,使得每一小段字符串中的字母 都不相同,并且希望分的段数尽量少. 然后,把这些小段按字典序排序后输出,中间由一个空格分隔. 例如:字符 ...
随机推荐
- c# mvc 简洁大气官网
后台管理 https://g.alicdn.com/idleFish-F2e/app-basic/item.html?itemid=580281597427&ut_sk=1.WCB2zfWM% ...
- 继承中的隐藏(hide)重写(Override)和多态(Polymorphism)
继承中的隐藏:(不要使用隐藏,语法没有错误但是开发项目时会被视为错误) 在继承类中完全保留基类中的函数名 //基类,交通工具 class Vehicle { public void Run() { C ...
- 使用JS实现快速排序
大致分三步: 1.找基准(一般是以中间项为基准) 2.遍历数组,小于基准的放在left,大于基准的放在right 3.递归 function quickSort(arr){ //如果数组<=1, ...
- JAVA java
{ 用法: java [-options] class [args...] (执行类) 或 java [-options] -jar jarfile [args...] ...
- vue-cli整合axios的几种方法
Vue这个框架现在在单页面应用方面非常受人欢迎. 基于vue-cli创建的项目怎么样才能更好地处理网络请求? 首选的应该就是axios了 这次给刚接触vue的新手介绍一下axios在vue中如何使用 ...
- (转)使用Apache的ab工具进行压力测试
转:http://www.cnblogs.com/luckyliu/archive/2012/03/04/2379306.html Apache附带的ab工具(本机使用的PHP环境是WAMP集成环境, ...
- hdu多校第五场1006 (hdu6629) string matching Ex-KMP
题意: 给你一个暴力匹配字符串公共前缀后缀的程序,为你对于某个字符串,暴力匹配的次数是多少. 题解: 使用扩展kmp构造extend数组,在扩展kmp中,设原串S和模式串T. extend[i]表示T ...
- 秒懂机器学习---k-近邻算法实战
秒懂机器学习---k-近邻算法实战 一.总结 一句话总结: k临近算法的核心就是:将训练数据映射成k维空间中的点 1.k临近算法怎么解决实际问题? 构建多维空间:每个特征是一维,合起来组成了一个多维空 ...
- 如何优雅的使用Objects.requireNonNull(T obj, String message)定制你的NPE异常
IDEA中习惯跟踪源码实现逻辑,多次碰到Objects.requireNonNull(T obj)这个方法,改方法主要用于提早判断对象是否为空,以便更早的抛出NPE 平时小组开发中强调程序健壮性,不允 ...
- eclispe 创建maven 项目:Could not resolve archetype org.apache.maven.archetypes
昨天新装eclispe 后,创建maven工程后出现 Could not resolve archetype org.apache.maven.archetypes:maven-archetype-q ...