2019.4.11 一题 XSY 1551 ——广义后缀数组(trie上后缀数组)
参考:http://www.mamicode.com/info-detail-1949898.html (log2)
https://blog.csdn.net/geotcbrl/article/details/50907662
https://blog.csdn.net/cdsszjj/article/details/79920308
https://blog.csdn.net/ez_yww/article/details/77072632
trie 上后缀数组想做的是把 trie 树每个节点开始到根的字符串排好序,求出 rk[ ] 和 sa[ ] ,需要的话也可以求出 ht[ ]。
普通后缀数组比较大小是通过倍增,即 [ i , i+k-1 ] 和 [ i+k , i+2k-1 ] 这两个位置作为两个关键字得到 [ i , i+2k-1 ] 的排名。这里也一样。
在树上的话, i+k 变成 i 点往上第 k 个祖先;所以先处理好祖先的倍增数组;顺便作出 dep[ ] ;
希望把每个 2t 时刻的 rk 数组存下来,所以令 rk[ t ][ i ] 表示第 t 次比较的时候 i 的排名。
当前这次比较的时候,tp[ ] 里要存按第二关键字排好序的序列,即 tp[ i ] 表示 “ 2t-1 次祖先排名为 i 的节点是谁 ” ;这里的 “排名为 i ” 指的是 “向上 2t-1 范围的字符串” 排名,即 rk[ t-1 ][ i ] 。
所以现在已知上一次的 sa 和 rk ,希望做出 tp[ ] 。只要记录一下每个点的 2t-1 次后代有些谁(注意可能有多个点),然后像平常一样把没有 2t-1 次祖先的点填在最前面,再按 sa 枚举,tp[ ++tot ] = 后代 即可。
原来用 vector 存了这些后代。但非常慢。每次通过 pre[ i ][ t-1 ] 连一个临接表出来会快很多。
然后希望作出 ht 。先要作出 ht[ i ][ 0 ] 。
现在真实的排名是最后一次做出的那个 rk 。所以 ht 是基于最后一次的 rk 和 sa 上求的。保留之前的 rk 可以用来求相邻两个字符串的 LCP 。就是像倍增一样。如果 rk[ t ][ x ] == rk[ t ][ y ] ,说明 i 和 j 向上 2t 个字符都是一样的,那么 ret+= bin[ t ] , x = pre[ x ][ t ] , y = pre[ y ][ t ] 。
当然也可以不保留这些 rk ,倍增地存一下每个点往上的哈希值也行。(所以比较两个串的复杂度是 log 而不是 log2 的!“二分” 和 “提取哈希值” 可以一起做!)
(这样有一个做法,就是启发式合并,合并子树的时候把小的部分的字符串二分地插入大的部分的字符串集合中。但支持 “提取某位置的元素” 和 “插入” 的数据结构会带 log ,所以是 log3 ,真遗憾)
注意桶要包含 0 位置。因为根向上没有字符,初始 rk 是 0 。同时注意输入的边上字符有 0 ,所以把输入都 +1 ,体现 “没有” 比 0 大。
然后这道题再套一个线段树合并就行了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define ll long long
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=2e5+,K=,bs=,md=1e9+;
int n,dep[N],hs[N][K+],pre[N][K],lj[N];
int sa[N],rk[N],ht[N][K+],bin[K+],lg[N];
int hd[N],xnt,to[N],nxt[N],rt[N],ans;
struct Node{
int x;
Node(int x=):x(x) {}
bool operator< (const Node &b)const
{return rk[x]<rk[b.x];}
};
set<Node> st[N];
set<Node>::iterator it,it2;
int get_lcp(int u,int v)
{
int ret=;
for(int t=lg[Mn(dep[u],dep[v])];t>=;t--)//Mn not Mx
if(pre[u][t]&&pre[v][t]&&hs[u][t]==hs[v][t])//if pre[][]
u=pre[u][t], v=pre[v][t], ret+=bin[t];
return ret;
}
bool cmp(int u,int v)
{
for(int t=lg[Mn(dep[u],dep[v])];t>=;t--)//Mn
if(pre[u][t]&&pre[v][t]&&hs[u][t]==hs[v][t])//pre[][]
u=pre[u][t], v=pre[v][t];
return hs[u][]<hs[v][];
}
void get_ht()
{
for(int i=;i<=n;i++)
ht[i][]=get_lcp(sa[i-],sa[i]);
for(int t=;t<=lg[n];t++)
for(int i=;i+bin[t]-<=n;i++)
ht[i][t]=Mn(ht[i][t-],ht[i+bin[t-]][t-]);
}
int qry_ht(int u,int v)
{
if(u==v)return dep[u];
u=rk[u]; v=rk[v]; if(u>v)swap(u,v);
u++; int d=lg[v-u+];
return Mn(ht[u][d],ht[v-bin[d]+][d]);
}
int mrg(int &u,int v)
{
int ret=;
if(st[u].size()<st[v].size())swap(u,v);
for(it=st[v].begin();it!=st[v].end();it++)
{
st[u].insert(*it);
it2=st[u].find(*it);
if(it2!=st[u].begin())
{
it2--; ret=Mx(ret,qry_ht((*it2).x,(*it).x));
it2++;
}
it2++; if(it2==st[u].end())continue;
ret=Mx(ret,qry_ht((*it2).x,(*it).x));
}
return ret;
}
void dfs(int cr)
{
rt[cr]=cr; st[rt[cr]].insert(Node(cr));
int ret=;
for(int i=hd[cr],v;i;i=nxt[i])
{
dfs(v=to[i]);
ret=Mx(ret,mrg(rt[cr],rt[v]));
}
if(st[rt[cr]].size()>)//if!!!
{
ans=Mx(ans,dep[cr]+ret);
}
}
int main()
{
freopen("recollection.in","r",stdin);
freopen("recollection.out","w",stdout);
n=rdn();
bin[]=;for(int i=;i<=K;i++)bin[i]=bin[i-]<<;
for(int i=;i<=n;i++)lg[i]=lg[i>>]+;
lj[]=;for(int i=;i<=n;i++)lj[i]=(ll)lj[i-]*bs%md;
int he=, tl=;
for(int i=;i<=n;i++)
{
pre[i][]=rdn();hs[i][]=rdn()+;//+1 for cmp empty
to[++xnt]=i; nxt[xnt]=hd[pre[i][]]; hd[pre[i][]]=xnt;
}
for(int i=;i<=n;i++)
{
dep[i]=dep[pre[i][]]+;
for(int t=,d=pre[i][];(d=pre[i][t-]);t++)
{
pre[i][t]=pre[d][t-];
hs[i][t]=((ll)hs[i][t-]*lj[bin[t-]]+hs[d][t-])%md;
}
}
for(int i=;i<=n;i++)sa[i]=i; sort(sa+,sa+n+,cmp);
for(int i=;i<=n;i++)rk[sa[i]]=i;
get_ht(); dfs();
printf("%d\n",ans);
return ;
}
log^2
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ls Ls[cr]
#define rs Rs[cr]
#define pb push_back
using namespace std;
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
int Mx(int a,int b){return a>b?a:b;}
int Mn(int a,int b){return a<b?a:b;}
const int N=2e5+,K=,M=N*K;
int n,dep[N],pre[N][K],hd[N],xnt,to[N],nxt[N];
int ht[K][N],rk[K][N],sa[N],tp[N],tx[N],lm,ans;
int bin[K],lg[N],rt[N],Ls[M],Rs[M],fr[M],sc[M],sm[M],tot;
int h2[N],xt2,t2[N],nt2[N];
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void ad2(int x,int y){t2[++xt2]=y;nt2[xt2]=h2[x];h2[x]=xt2;}
void Rsort(int n,int nm,int t)
{
for(int i=;i<=nm;i++)tx[i]=;////// 0 !!!! for empty
for(int i=;i<=n;i++)tx[rk[t][i]]++;
for(int i=;i<=nm;i++)tx[i]+=tx[i-];
for(int i=n;i;i--)sa[tx[rk[t][tp[i]]]--]=tp[i];
}
void get_sa(int n,int nm)
{
for(int i=;i<=n;i++)tp[i]=i;
Rsort(n,nm,);
for(int t=;;t++)
{
xt2=; memset(h2,,sizeof h2);
for(int i=;i<=n;i++)
if(pre[i][t-])ad2(pre[i][t-],i);
int tot=;
for(int i=;i<=n;i++)
if(!pre[i][t-])tp[++tot]=i;
for(int i=;i<=n;i++)
for(int j=h2[sa[i]];j;j=nt2[j])
tp[++tot]=t2[j];
Rsort(n,nm,t-); rk[t][sa[]]=nm=;
for(int i=;i<=n;i++)
{
int cr=sa[i], u=pre[cr][t-], v=pre[sa[i-]][t-];
rk[t][cr]=(rk[t-][cr]==rk[t-][sa[i-]]&&rk[t-][u]==rk[t-][v])?nm:++nm;
}
lm=t; if(nm==n)break;
}
}
int get_lcp(int u,int v)
{
int ret=;
for(int t=lm;t>=;t--)//t=lm!!
if(pre[u][t]&&pre[v][t]&&rk[t][u]==rk[t][v])//pre
u=pre[u][t], v=pre[v][t], ret+=bin[t];
return ret;
}
void get_ht()
{
for(int i=;i<=n;i++)ht[][i]=get_lcp(sa[i-],sa[i]);
for(int t=;t<=lg[n];t++)
for(int i=;i<=n;i++)
ht[t][i]=Mn(ht[t-][i],ht[t-][i+bin[t-]]);
}
int qry_ht(int l,int r)
{
if(l==r)return dep[sa[l]];
if(l>r)swap(l,r); l++; int d=lg[r-l+];
int ret=Mn(ht[d][l],ht[d][r-bin[d]+]);
return ret;
}
void build(int l,int r,int &cr,int p)
{
cr=++tot; fr[cr]=sc[cr]=p;
if(l==r)return; int mid=l+r>>;
if(p<=mid)build(l,mid,ls,p);
else build(mid+,r,rs,p);
}
void mrg(int l,int r,int &cr,int pr)
{
if(!cr){cr=pr;return;} if(!pr)return;
int mid=l+r>>;
mrg(l,mid,ls,Ls[pr]); mrg(mid+,r,rs,Rs[pr]);
fr[cr]=(ls?fr[ls]:fr[rs]); sc[cr]=(rs?sc[rs]:sc[ls]);
sm[cr]=Mx(sm[ls],sm[rs]);
if(fr[ls]&&fr[rs])
sm[cr]=Mx(sm[cr],qry_ht(sc[ls],fr[rs]));
}
void dfs(int cr)
{
build(,n,rt[cr],rk[lm][cr]);
for(int i=hd[cr],v;i;i=nxt[i])
{
dfs(v=to[i]);
mrg(,n,rt[cr],rt[v]);
}
if(hd[cr])
{
ans=Mx(ans,dep[cr]+sm[rt[cr]]);
}
}
int main()
{
freopen("recollection.in","r",stdin);
freopen("recollection.out","w",stdout);
n=rdn();
for(int i=;i<=n;i++)lg[i]=lg[i>>]+;
bin[]=;for(int i=;bin[i-]<=n;i++)bin[i]=bin[i-]<<;
for(int i=;i<=n;i++)
{
int fa=rdn(); rk[][i]=rdn()+;//+1!!!!! for cmp with empty
dep[i]=dep[fa]+; pre[i][]=fa; add(fa,i);
for(int t=,d=fa;(d=pre[i][t-]);t++)
pre[i][t]=pre[d][t-];
}
get_sa(n,); get_ht();
dfs();
printf("%d\n",ans);
return ;
}
log
2019.4.11 一题 XSY 1551 ——广义后缀数组(trie上后缀数组)的更多相关文章
- Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解
Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解 题目地址:https://codingcompetitions.withgoogle.com/kickstar ...
- Good Bye 2019(前五题题解)
这套也是后来补得. 我太菜了,第三题就卡着了.想了好久才做出来,要是参加了绝对掉分. D题是人生中做完的第一道交互题,不容易. 比赛传送门 A.Card Game 题目大意:一共有n张互不相同的牌,玩 ...
- 2019年11个javascript机器学习库
Credits: aijs.rocks 虽然python或r编程语言有一个相对容易的学习曲线,但是Web开发人员更喜欢在他们舒适的javascript区域内做事情.目前来看,node.js已经开始向每 ...
- 2019阿里校招测评题,光明小学完全图最短路径问题(python实现)
题目:光明小学的小朋友们要举行一年一度的接力跑大赛了,但是小朋友们却遇到了一个难题:设计接力跑大赛的线路,你能帮助他们完成这项工作么?光明小学可以抽象成一张有N个节点的图,每两点间都有一条道路相连.光 ...
- tmux使用——2019年11月20日16:40:15
1.tmux 命令行的典型使用方式是,打开一个终端窗口(terminal window,以下简称"窗口"),在里面输入命令.用户与计算机的这种临时的交互,称为一次"会话& ...
- hdu6578 2019湖南省赛D题Modulo Nine 经典dp
目录 题目 解析 AC_Code @ 题目 第一题题意是一共有{0,1,2,3}四种数字供选择,问有多少个长度为n的序列满足所有m个条件,每个条件是说区间[L,R]内必须有恰好x个不同的数字. 第二题 ...
- 打开随身U盘_办公专用盘 2019年11月29日
;;; ; 打开随身U盘_办公专用盘 2019年11月29日 ; https://www.autoahk.com/?p=16553; https://www.cnblogs.com/delphixx/ ...
- 'Rem EverythingAutoSetup.VBS 安装Everything的VBS脚本 2019年11月25日写
'Rem EverythingAutoSetup.VBS 安装Everything的VBS脚本 2019年11月25日写 'Rem Everything是voidtools开发的一款本地NTFS文件和 ...
- 键盘和鼠标闲置超时时关闭显示器并锁定电脑桌面的AutoHotkey脚本 2019年11月24日写
/* 键盘和鼠标闲置超时时关闭显示器并锁定电脑桌面的AutoHotkey脚本 2019年11月24日写 在电脑桌面锁定时移动鼠标就会显示登录界面,此时即使超过电源设置的时间电脑也不会关闭显示器使得屏幕 ...
随机推荐
- 20165326 java第七周学习笔记
第七周学习笔记 MySQL(数据管理系统)学习 知识点总结: 不能通过关闭MySQL数据库服务器所占用的命令行窗口来关闭MySQL数据库. 如果MySQL服务器和MySQL管理工具驻留在同一台计算机上 ...
- 20165326 学习基础和c语言基础调查
学习基础和c语言基础调查 一.关于个人技能 阅读了娄老师关于做中学的文章,我想起了自己之前学习技能的经历. 从小到大我学过的东西不少,除学校的教育课程外,我还参加过各种兴趣班,书法.绘画.舞蹈.吉他. ...
- js 数组去重的几种方式及原理
let arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,' ...
- Vuex的学习笔记一
以下的解释,是在知乎看到的,感觉粗俗易懂. 组件之间的作用域独立,而组件之间经常又需要传递数据. A 为父组件,下面有子组件 B 和 C. A 的数据可以通过 props 传递给 B 和 C.A 可以 ...
- 简单的SpringWebFlow例子及遇到的问题
这段时间在看<Spring 实战>里面有讲Spring Web Flow,觉得里面的例子过于复杂,不适合新手,于是在网上找了个例子,跟着写 以下是项目的目录,我是基于maven搭建项目的 ...
- django面试四
Django的优点 功能完善.要素齐全:自带大量常用工具和框架(比如分页,auth,权限管理), 适合快速开发企业级网站. 完善的文档:经过十多年的发展和完善,Django有广泛的实践案例和完善的在线 ...
- Day7作业及默写
1. 判断一个数是否是水仙花数, 水仙花数是一个三位数, 三位数的每一位的三次方的和还等于这个数. 那这个数就是一个水仙花数. 例如: 153 = 1**3 + 5**3 + 3**3 InputNu ...
- 转:C++ 类的静态成员详细讲解
在C++中,静态成员是属于整个类的而不是某个对象,静态成员变量只存储一份供所有对象共用.所以在所有对象中都可以共享它.使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节 ...
- iccv文献引用
1.@inproceedings:会议 2.@article:期刊 3.@incollection:书 4.@misc:啥不是 author的名字书写: pdf显示为:G. Wang bibtex中: ...
- OTG作为大容量设备
/********************************************************************************* * OTG作为大容量设备 * 说明 ...