题目

给你一棵每条边从父亲指向儿子的树,每条边上面有一个字母。

从树上的任意一点出发,走出的路径就是对应一个子串。

(这不是\(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的字符串的更多相关文章

  1. GDOI2014模拟pty爬山(mountain)

    pty爬山(mountain) 在Pty学校附近,有一座名之为岳之麓的高山.Pty很喜欢和(哔--)一起爬山.山的平面模型如下:山由一个顶点集:A1,A2-An给定,保证Ai的x单调递增.我们将Ai和 ...

  2. 【GDOI2014模拟】JZOJ2020年8月14日提高组 服务器

    [GDOI2014模拟]JZOJ2020年8月14日提高组 服务器 题目 Time and Memory Limits Description 我们需要将一个文件复制到n个服务器上,这些服务器的编号为 ...

  3. 【GDOI2014模拟】JZOJ2020年8月14日T2 网格

    [GDOI2014模拟]JZOJ2020年8月14日T2 网格 题目 Time and Memory Limits Description 某城市的街道呈网格状,左下角坐标为A(0, 0),右上角坐标 ...

  4. bzoj3756: Pty的字符串

    #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #i ...

  5. 计蒜客蓝桥杯模拟赛 后缀字符串:STL_map+贪心

    问题描述 一天蒜头君得到 n 个字符串 si​,每个字符串的长度都不超过 10. 蒜头君在想,在这 n 个字符串中,以 si​ 为后缀的字符串有多少个呢? 输入格式 第一行输入一个整数 n. 接下来  ...

  6. 洛谷 P3695 CYaRon!语 题解 【模拟】【字符串】

    大模拟好啊! 万一远古计算机让我写个解释器还真是得爆零了呢. 题目背景 「千歌です」(我是千歌).「曜です」(我是曜).「ルビィです」(我是露比).「3人合わせて.We are CYaRon! よろし ...

  7. Pty的字符串(string)

    题目描述 在神秘的东方有一棵奇葩的树,它有一个固定的根节点(编号为1).树的每条边上都是一个字符,字符为a,b,c中的一个,你可以从树上的任意一个点出发,然后沿着远离根的边往下行走,在任意一个节点停止 ...

  8. Hidden (NOIP模拟赛)(字符串模拟QAQ)

    原题传送门 神奇的题目诶 原来以为字符串比较一定要O(NlogN) 结果发现可以均摊O(N) 首先我们来讲一讲原理 我们有3个指针i,j,k i=0,j=1,k=0 一开始我们不断对k+1直到找到ch ...

  9. 常州模拟赛d4t3 字符串划分

    题目描述 给你一串由小写字母组成的字符串,希望你把它划分成一些小段,使得每一小段字符串中的字母 都不相同,并且希望分的段数尽量少. 然后,把这些小段按字典序排序后输出,中间由一个空格分隔. 例如:字符 ...

随机推荐

  1. C# WinfForm 控件之dev报表 XtraReport (八)动态生成报表

    功能说明:生成一个报表文件DV1,保存到本地AA.rep,再重新加载这个文件到DV2 1.布局如下图: panel1 上有三个button panel2上有个documentView dv1 pane ...

  2. JQuery on绑定click无效的的bug解决办法?

    如果你在移动端使用zepto.js, touch 来代替click事件,那就没有上述标题的问题了.如果你仍然使用了click,在点击事件中苹果机就可能出现无效的结果. 第一种方法: 解决的方法很巧妙, ...

  3. Unity中动态绘制圆柱体

    问题背景 上次写了动态绘制立方体,这最近又来了新功能,绘制圆柱(风筒),要求是给了很多节点,根据节点去动态绘制风筒,风筒就是圆柱连接而成的,可以理解为管道,还有就是拐角处注意倒角,圆润过度过来. 实现 ...

  4. $router和$route的区别,路由跳转方式name 、 path 和传参方式params 、query的区别

    一.$router和$route的区别 $router : 是路由操作对象,只写对象$route : 路由信息对象,只读对象 例子://$router操作 路由跳转 this.$router.push ...

  5. JS变量1

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. jq容易混淆点

    jQuery链式编程: 原:把当前元素颜色修改为红色,其余兄弟颜色不变 $(this).css("color",'red') ; $(this).siblings().css(&q ...

  7. python指定概率随机取值 理解np.random.seed()

    python指定概率随机取值参考如下: 下面是利用 np.random.choice()指定概率取样的例子: np.random.seed(0) p = np.array([0.1, 0.0, 0.7 ...

  8. ElementUI的Loading组件 —— 想实现在请求后台数据之前开启Loading组件,请求成功或失败之后,关闭Loading组件

    我在实际项目开发中,遇到了这个需求,记录一下~~~~~~ 在ElementUI官网上有几种实现Loading的方法,但官网上是在一个方法里写了开启与关闭组件,所以可以根据官网的实现方法进行一个封装,便 ...

  9. window操作命令

    netstat -ano 查看所有端口 netstat -ano|findstr "8005"  查看指定端口

  10. wp api jwt 403 (Forbidden) -- JWT is not configurated properly, please contact the admin

    需要在 wp-config.php 文件中配置 define('JWT_AUTH_SECRET_KEY', 'your-top-secrect-key'); 參考 403 (Forbidden) -- ...