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日写 在电脑桌面锁定时移动鼠标就会显示登录界面,此时即使超过电源设置的时间电脑也不会关闭显示器使得屏幕 ...
随机推荐
- shell连接ubuntu流程
{ubuntu}下载 sudo apt-get install tasksel {选择安装SSH server服务)[有些ubuntu默认是安装好ssh的,只是没有配置,和开启ssh服务] s ...
- python redis操作数据库方法
Redis redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorte ...
- Kaggle(1):数据挖掘的基本流程
我觉得做任何事情,一定要有章法.对于学习类的事情,就是要有框架.第一次打Kaggle比赛,我的一个重要收获就是初步搞清楚了打这类比赛的框架. 可以分为以下六步:理解问题.分析问题.算法选择.结果评价. ...
- git解决not a git repository
意思是说没有库,需要你创建 git init zzz zzz文件夹就会出现在你的项目中,里面就会有.git文件,将里面的.git剪切到与项目同一级中 关注微信小程序
- 阿里云配置ssh
1.申请证书,从我的域名列表 右侧的操作栏中点击- ssl 2.配置 nginx config -> /etc/nginx/sites-enabled/default 底部添加:(我的是dj ...
- SSL/TLS Server supports TLSv1.0
远程登录服务器后打开注册表编辑器,点开HKEY-LOCAL-MACHINE,SYSTEM,CURRENTCONTROLSET下的Control 找到SecurityProviders下的SCHANNE ...
- 2.19 cookie相关操作
2.19 cookie相关操作 前言虽然cookie相关操作在平常ui自动化中用得少,偶尔也会用到,比如登录有图形验证码,可以通过绕过验证码方式,添加cookie方法登录.登录后换账号登录时候,也可作 ...
- LOD,听起来很牛逼的样子
<!DOCTYPE html> <html lang="en"> <head> <title>three.js webgl - le ...
- 【转载】 AI会议的总结(by南大周志华)
原文地址: https://blog.csdn.net/LiFeitengup/article/details/8441054 最近在查找期刊会议级别的时候发现这篇博客,应该是2012年之前的内容,现 ...
- Java IntelliJ IDEA 不能显示项目里的文件结构
方法一: 关闭IDEA, 然后删除项目文件夹下的.idea文件夹 重新用IDEA工具打开项目 方法二: 菜单:File -> Invalidate Caches / Restart