BZOJ3926 ZJOI2015诸神眷顾的幻想乡(广义后缀自动机)
对多串建立SAM的一种方法是建trie再对trie建SAM。构造方式分为在线(也即不建trie而是依次插入每个串,或在trie上dfs)和离线(也即建好trie再bfs)。其中离线构造与单串的构造方式几乎没有区别,将父亲所在节点作为last即可,按bfs序建SAM可以避免出现要转移到的状态已经存在的情况。而在线构造时则需要考虑这点,若存在除了不用新建节点其余操作也与单串区别不大。
这种做法相比加分隔符建SAM,一方面避免了存在分隔符的尴尬状态的存在,方便于统计一些东西。(比如本题的本质不同子串数量,如果加分隔符就还要考虑一下right集合,不过好像影响也不是特别大?)另一方面这样建出的SAM总状态数是O(|T|)的,转移函数也即边数是O(|T||A|)的,虽然在线构造的复杂度是O(G(T)+|T||A|),但更简单的离线构造可以做到理论最低复杂度O(|T||A|)(|T|为trie总节点数,|A|为字符集大小,G(T)为trie上所有叶子深度之和,据论文,我当然啥都不知道);而加分隔符建SAM的上述所有复杂度都是O(G(T))。也就是说这种做法在直接给出trie的题中会吊打加分隔符。
对于本题,如果离线构造,拎出每个叶子作为根得到的trie合并起来建SAM即可。在线构造只要以每个叶子为根dfs并插进SAM。
在线:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 4000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,a[N],p[N],degree[N],id[N],son[N][10],fail[N],len[N],t,cnt=1;
ll ans;
struct data{int to,nxt;
}edge[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
int ins(int c,int p)
{
if (son[p][c])
{
int q=son[p][c];
if (len[p]+1==len[q]) return q;
else
{
int y=++cnt;
len[y]=len[p]+1;
memcpy(son[y],son[q],sizeof(son[q]));
fail[y]=fail[q],fail[q]=y;
while (son[p][c]==q) son[p][c]=y,p=fail[p];
return y;
}
}
else
{
int x=++cnt;len[x]=len[p]+1;
while (!son[p][c]&&p) son[p][c]=x,p=fail[p];
if (!p) fail[x]=1;
else
{
int q=son[p][c];
if (len[p]+1==len[q]) fail[x]=q;
else
{
int y=++cnt;
len[y]=len[p]+1;
memcpy(son[y],son[q],sizeof(son[q]));
fail[y]=fail[q],fail[x]=fail[q]=y;
while (son[p][c]==q) son[p][c]=y,p=fail[p];
}
}
return x;
}
}
void dfs(int k,int from)
{
id[k]=ins(a[k],id[from]);
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) dfs(edge[i].to,k);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj3926.in","r",stdin);
freopen("bzoj3926.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read();read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
degree[x]++,degree[y]++;
}
id[0]=1;
for (int i=1;i<=n;i++)
if (degree[i]==1) dfs(i,0);
for (int i=1;i<=cnt;i++) ans+=len[i]-len[fail[i]];
cout<<ans;
return 0;
}
离线:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 4000010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,c,a[N],p[N],degree[N],id[N],son[N][10],q[N],trie[N][10],fail[N],len[N],t,cnt;
ll ans;
struct data{int to,nxt;
}edge[N];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
int ins(int c,int p)
{
int x=++cnt;len[x]=len[p]+1;
while (!son[p][c]&&p) son[p][c]=x,p=fail[p];
if (!p) fail[x]=1;
else
{
int q=son[p][c];
if (len[p]+1==len[q]) fail[x]=q;
else
{
int y=++cnt;
len[y]=len[p]+1;
memcpy(son[y],son[q],sizeof(son[q]));
fail[y]=fail[q],fail[x]=fail[q]=y;
while (son[p][c]==q) son[p][c]=y,p=fail[p];
}
}
return x;
}
void dfs(int k,int from)
{
if (!trie[id[from]][a[k]]) trie[id[from]][a[k]]=++cnt;
id[k]=trie[id[from]][a[k]];
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from) dfs(edge[i].to,k);
}
void bfs()
{
int head=0,tail=1;q[1]=0;id[0]=1;cnt=1;
do
{
int x=q[++head];
for (int i=0;i<c;i++)
if (trie[x][i])
{
q[++tail]=trie[x][i];
id[trie[x][i]]=ins(i,id[x]);
}
}while (head<tail);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("bzoj3926.in","r",stdin);
freopen("bzoj3926.out","w",stdout);
const char LL[]="%I64d\n";
#else
const char LL[]="%lld\n";
#endif
n=read(),c=read();
for (int i=1;i<=n;i++) a[i]=read();
for (int i=1;i<n;i++)
{
int x=read(),y=read();
addedge(x,y),addedge(y,x);
degree[x]++,degree[y]++;
}
for (int i=1;i<=n;i++)
if (degree[i]==1) dfs(i,0);
bfs();
for (int i=1;i<=cnt;i++) ans+=len[i]-len[fail[i]];
cout<<ans;
return 0;
}
离线甚至慢了一倍,可能是要先建trie的原因。
BZOJ3926 ZJOI2015诸神眷顾的幻想乡(广义后缀自动机)的更多相关文章
- bzoj3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机模板
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #d ...
- 【BZOJ3926】[Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机
[BZOJ3926][Zjoi2015]诸神眷顾的幻想乡 Description 幽香是全幻想乡里最受人欢迎的萌妹子,这天,是幽香的2600岁生日,无数幽香的粉丝到了幽香家门前的太阳花田上来为幽香庆祝 ...
- BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 [广义后缀自动机 Trie]
3926: [Zjoi2015]诸神眷顾的幻想乡 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1124 Solved: 660[Submit][S ...
- BZOJ.3926.[ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)
题目链接 要对多个串同时建立SAM,有两种方法: 1.将所有串拼起来,中间用分隔符隔开,插入字符正常插入即可. 2.在这些串的Trie上建SAM.实际上并不需要建Trie,还是只需要正常插入(因为本来 ...
- 洛谷P3346 [ZJOI2015]诸神眷顾的幻想乡(广义后缀自动机)
题意 题目链接 Sol 广义SAM的板子题. 首先叶子节点不超过20,那么可以直接对每个叶子节点为根的子树插入到广义SAM中. 因为所有合法的答案一定是某个叶子节点为根的树上的一条链,因此这样可以统计 ...
- BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡 广义后缀自动机 后缀自动机 字符串
https://www.lydsy.com/JudgeOnline/problem.php?id=3926 广义后缀自动机是一种可以处理好多字符串的一种数据结构(不像后缀自动机只有处理一到两种的时候比 ...
- BZOJ 3926 [Zjoi2015]诸神眷顾的幻想乡 ——广义后缀自动机
神奇的性质,叶子节点不超过20个. 然后把这些节点提出来构成一颗新树,那么这些树恰好包含了所有的情况. 所以直接广义后缀自动机. 然后统计本质不同的字符串就很简单显然了. #include <c ...
- [ZJOI2015]诸神眷顾的幻想乡 广义后缀自动机_DFS_语文题
才知道题目中是只有20个叶子节点的意思QAQ.... 这次的广义后缀自动机只是将 last 设为 1, 并重新插入. 相比于正统的写法,比较浪费空间. Code: #include <cstdi ...
- BZOJ 3926: [Zjoi2015]诸神眷顾的幻想乡(广义后缀自动机 多串)
因为任何一条路径都可以看做某两个叶子节点之间路径的一部分,然后分别把20个叶节点当作根,把整棵树看作trie树,那么一条路径就能看作是从根到某个点这一条路的后缀,构建SAM就能维护不同子串的个数了. ...
- 【BZOJ3926】诸神眷顾的幻想乡(后缀自动机)
[BZOJ3926]诸神眷顾的幻想乡(后缀自动机) 题面 BZOJ 题解 广义后缀自动机啦 求多个串的不同子串个数? 当然是后缀自动机,最后只要把\(longest-parent.longest\)求 ...
随机推荐
- Python——EM(期望极大算法)教学(附详细代码与注解)
今天,我们详细的讲一下EM算法. 前提准备 Jupyter notebook 或 Pycharm 火狐浏览器或谷歌浏览器 win7或win10电脑一台 网盘提取csv数据 需求分析 实现高斯混合模型的 ...
- Centos7安装pip或pip3
1.使用Python2安装pip wget wget --no-check-certificate https://pypi.python.org/packages/source/p/pip/pip- ...
- SPring boot jpa 封装查询条件
最近使用spring data jpa做了两个项目,对于动态查询的不友好做了个类似hibernate的封装,记录也分享下 首先定义一个所有条件的容器,继承Specification /** * 定义一 ...
- php dirname 的简单使用
dirname dirname-返回路径中的目录部分 说明 dirname(string$path) :string 给出一个包含有指向一个文件的全路径的字符串,本函数返回去掉文件名后的目录名. 参数 ...
- MSSQL记录表字段数据变化的相关SQl
在软件实施过程中,也许会有这样的问题: 表中数据出现非预期的结果,此时不确定是程序问题,哪个程序,存储过程,触发器.. 或还是人为修改的结果,此时可以用触发器对特定的表字段做跟踪监视,记录每次新增,修 ...
- (原)在anaconda3+ubuntu16.04中编译Pose flow中的deepmatching动态库
转载请注明出处: https://www.cnblogs.com/darkknightzh/p/11285239.html 参考网址: https://github.com/YuliangXiu/Po ...
- docker gitlab-runner的安装
参考: Run GitLab Runner in a container 前面介绍了gitlab-ce的安装,下面是gitlab-runner的安装,同样还是安装docker版本. 1.下载 dock ...
- axios之Vue请求初始化数据放在Created还是Mounted?
先分析下生命周期 beforecreated:el 和 data 并未初始化 created:完成了 data 数据的初始化,el没有 beforeMount:完成了 el 和 data 初始化 mo ...
- 第07节-开源蓝牙协议BTStack框架代码阅读(上)
首先来看一下,对于硬件操作,它是如何来进行处理的.在上篇文章中曾说过,在main函数里面它会调用硬件相关的代码,调用操作系统相关的代码.在BTStack中,可以搜索一下main.c,将会发现有很多ma ...
- flask实战-个人博客-模板 --
模板 personalBlog采用典型的博客布局,左侧三分之二为主体,显示文章列表.正文:右侧三分之一为边栏,显示分为类列表.社交链接等.现在的工作是将HTML文件加工为模板,并创建对应的表单类,在模 ...