参考: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上后缀数组)的更多相关文章

  1. Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解

    Google Kick Start 2019 C轮 第一题 Wiggle Walk 题解 题目地址:https://codingcompetitions.withgoogle.com/kickstar ...

  2. Good Bye 2019(前五题题解)

    这套也是后来补得. 我太菜了,第三题就卡着了.想了好久才做出来,要是参加了绝对掉分. D题是人生中做完的第一道交互题,不容易. 比赛传送门 A.Card Game 题目大意:一共有n张互不相同的牌,玩 ...

  3. 2019年11个javascript机器学习库

    Credits: aijs.rocks 虽然python或r编程语言有一个相对容易的学习曲线,但是Web开发人员更喜欢在他们舒适的javascript区域内做事情.目前来看,node.js已经开始向每 ...

  4. 2019阿里校招测评题,光明小学完全图最短路径问题(python实现)

    题目:光明小学的小朋友们要举行一年一度的接力跑大赛了,但是小朋友们却遇到了一个难题:设计接力跑大赛的线路,你能帮助他们完成这项工作么?光明小学可以抽象成一张有N个节点的图,每两点间都有一条道路相连.光 ...

  5. tmux使用——2019年11月20日16:40:15

    1.tmux 命令行的典型使用方式是,打开一个终端窗口(terminal window,以下简称"窗口"),在里面输入命令.用户与计算机的这种临时的交互,称为一次"会话& ...

  6. hdu6578 2019湖南省赛D题Modulo Nine 经典dp

    目录 题目 解析 AC_Code @ 题目 第一题题意是一共有{0,1,2,3}四种数字供选择,问有多少个长度为n的序列满足所有m个条件,每个条件是说区间[L,R]内必须有恰好x个不同的数字. 第二题 ...

  7. 打开随身U盘_办公专用盘 2019年11月29日

    ;;; ; 打开随身U盘_办公专用盘 2019年11月29日 ; https://www.autoahk.com/?p=16553; https://www.cnblogs.com/delphixx/ ...

  8. 'Rem EverythingAutoSetup.VBS 安装Everything的VBS脚本 2019年11月25日写

    'Rem EverythingAutoSetup.VBS 安装Everything的VBS脚本 2019年11月25日写 'Rem Everything是voidtools开发的一款本地NTFS文件和 ...

  9. 键盘和鼠标闲置超时时关闭显示器并锁定电脑桌面的AutoHotkey脚本 2019年11月24日写

    /* 键盘和鼠标闲置超时时关闭显示器并锁定电脑桌面的AutoHotkey脚本 2019年11月24日写 在电脑桌面锁定时移动鼠标就会显示登录界面,此时即使超过电源设置的时间电脑也不会关闭显示器使得屏幕 ...

随机推荐

  1. Building Tablet PC Applications ROB JARRETT

    Building Tablet PC Applications ROB JARRETT Tablet PC 开发,有需要PDF的留下邮箱 目录This text was added by using ...

  2. HDU 6066 17多校3 RXD's date(超水题)

    Problem Description As we all know that RXD is a life winner, therefore he always goes out, dating w ...

  3. TAF /tars必修课(一):整体架构理解

    来自零点智能社区 一.前言 TAF,一个后台逻辑层的高性能RPC框架,目前支持C++,Java, node 三种语言, 往后可能会考虑提供更多主流语言的支持如 go等,自定义协议JCE,同时也支持HT ...

  4. Python学习笔记第二十三周(Flask架构)

    目录: 一.变量引用 内容: 备注:PyCharm小技巧,comm+alt+l  自动修改格式,comm+alt+return  向上添加新行 一.变量引用 1.url生成 from flask im ...

  5. FreeModbus LINUXTCP Compile ERROR

    /********************************************************************************* * FreeModbus LINU ...

  6. Unity游戏开发常用的一些函数用法

    Unity游戏开发常用函数 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享. ...

  7. 利用Git hub创建博客

    1.准备工作 到Git官网 下载Git,并且配置环境变量 2.注册Git Hub账号 到Git Hub官网注册相关账号,比如本文的账号为13627225740L,并至New repository创建仓 ...

  8. root/base/stem概念

    The verb root (or base form): 1.is the same as the infinitive不定式 (e.g., to dive, to jump, to wonder) ...

  9. 快速排序 C语言实现

    转载于> http://blog.chinaunix.net/uid-26404477-id-3329885.html 总的关键字比较次数:O(nlgn) 尽管快速排序的最坏时间为O(n2),但 ...

  10. JSM的topic和queue的区别

    在JMS(Java消息服务)中,Topic实现publish和subscribe语义.一条消息被publish时,它将发到所有感兴趣的订阅者,所以零到多个 subscriber(电脑词汇中解释为“用户 ...