bzoj3413
SAM好题,显然我们不能与每个后缀都去算LCP
考虑对询问串每一位算贡献,先构建出逆序构建自动机,这样我们得到了原串的后缀树(parent树)
根据parent树的定义,一个节点对应字符串出现的位置对应该节点的right集合也就是子树right集合的并
某些节点代表了一个后缀,我们从开头到结尾编号为1~n;这样求出每个节点的子树内,代表后缀的节点所代表的后缀编号最小是多少,记作mi[]
然后对于每个询问串在自动机上匹配(逆序),设最终匹配到的点为x
由于每个子串一定是某个后缀的某个前缀
如果匹配成功了,说明匹配到mi[x]这个后缀就结束了,否则会一直匹配下去,我们假设匹配到n+1结束
设最终匹配到的后缀为m,显然,从x到root上我们计算每条边的贡献
对于边(fa[a],a),边上的每个字符的比较次数贡献显然就是a子树内代表后缀编号小于等于m的节点个数
裸的想法可以用dfs序+主席树来完成
最后我们答案还要+x-1,因为比较失败也算是一次比较……
- type node=record
- po,next:longint;
- end;
- point=record
- l,r,s:longint;
- end;
- var tree:array[..*] of point;
- e:array[..] of node;
- go:array[..,''..''] of longint;
- mi,b,h,p,l,r,fa,mx,w:array[..] of longint;
- cl,n,m,x,y,i,j,k,last,t,len:longint;
- fl:boolean;
- s:ansistring;
- ans:int64;
- function lowbit(x:longint):longint;
- begin
- exit(x and (-x));
- end;
- function min(a,b:longint):longint;
- begin
- if a>b then exit(b) else exit(a);
- end;
- procedure ins(x,y:longint);
- begin
- inc(len);
- e[len].po:=y;
- e[len].next:=p[x];
- p[x]:=len;
- end;
- procedure add(c:char);
- var p,q,np,nq:longint;
- begin
- p:=last;
- inc(t); np:=t; last:=t;
- mx[np]:=mx[p]+;
- w[np]:=i;
- while (p<>) and (go[p,c]=) do
- begin
- go[p,c]:=np;
- p:=fa[p];
- end;
- if p= then fa[np]:=
- else begin
- q:=go[p,c];
- if mx[q]=mx[p]+ then fa[np]:=q
- else begin
- inc(t); nq:=t;
- mx[nq]:=mx[p]+;
- go[nq]:=go[q];
- fa[nq]:=fa[q];
- fa[q]:=nq; fa[np]:=nq;
- while go[p,c]=q do
- begin
- go[p,c]:=nq;
- p:=fa[p];
- end;
- end;
- end;
- end;
- procedure dfs(x:longint);
- var i,y:longint;
- begin
- inc(len);
- b[len]:=x;
- mi[x]:=w[x];
- if w[x]= then mi[x]:=;
- // writeln(x,' ',fa[x],':',w[x]);
- l[x]:=len;
- i:=p[x];
- while i<> do
- begin
- y:=e[i].po;
- dfs(y);
- mi[x]:=min(mi[x],mi[y]);
- i:=e[i].next;
- end;
- r[x]:=len;
- // writeln(mi[x]);
- end;
- function build(l,r:longint):longint;
- var m,q:longint;
- begin
- inc(len);
- if l=r then exit(len)
- else begin
- q:=len;
- m:=(l+r) shr ;
- tree[q].l:=build(l,m);
- tree[q].r:=build(m+,r);
- exit(q);
- end;
- end;
- function work(l,r,last,x:longint):longint;
- var m,q:longint;
- begin
- inc(len);
- q:=len;
- if l=r then tree[q].s:=tree[last].s+
- else begin
- m:=(l+r) shr ;
- if x<=m then
- begin
- tree[q].r:=tree[last].r;
- tree[q].l:=work(l,m,tree[last].l,x);
- end
- else begin
- tree[q].l:=tree[last].l;
- tree[q].r:=work(m+,r,tree[last].r,x);
- end;
- tree[q].s:=tree[tree[q].l].s+tree[tree[q].r].s;
- end;
- exit(q);
- end;
- function ask(l,r,p,q:longint):longint;
- var m:longint;
- begin
- if (x>=r) then exit(tree[q].s-tree[p].s)
- else begin
- m:=(l+r) shr ;
- if x<=m then exit(ask(l,m,tree[p].l,tree[q].l))
- else exit(tree[tree[q].l].s-tree[tree[p].l].s+ask(m+,r,tree[p].r,tree[q].r));
- end;
- end;
- begin
- readln(n);
- readln(s);
- last:=; t:=;
- for i:=n downto do
- add(s[i]);
- for i:= to t do
- if fa[i]<> then ins(fa[i],i);
- len:=;
- dfs();
- readln(m);
- len:=;
- h[]:=build(,n);
- for i:= to t do
- if w[b[i]]= then h[i]:=h[i-]
- else h[i]:=work(,n,h[i-],w[b[i]]);
- for i:= to m do
- begin
- readln(s);
- len:=length(s);
- j:=;
- fl:=true;
- cl:=;
- for k:=len downto do
- if go[j,s[k]]= then
- begin
- while (go[j,s[k]]=) and (j>) do
- begin
- j:=fa[j];
- cl:=mx[j];
- end;
- if j= then j:=
- else begin
- inc(cl);
- j:=go[j,s[k]];
- end;
- fl:=false;
- end
- else begin
- inc(cl);
- j:=go[j,s[k]];
- end; //cl表示询问串从头开始最长匹配的长度
- if fl then x:=mi[j] else x:=n+;
- ans:=;
- if j> then
- begin
- y:=cl-mx[fa[j]]; // 这里要注意
- ans:=ans+int64(y)*int64(ask(,n,h[l[j]-],h[r[j]]));
- j:=fa[j];
- end;
- while j> do
- begin
- y:=mx[j]-mx[fa[j]];
- ans:=ans+int64(y)*int64(ask(,n,h[l[j]-],h[r[j]]));
- j:=fa[j];
- end;
- writeln(ans+x-);
- end;
- end.
bzoj3413的更多相关文章
- 【BZOJ3413】匹配(后缀自动机,线段树合并)
[BZOJ3413]匹配(后缀自动机,线段树合并) 题面 BZOJ 题解 很好的一道题目. 做一个转化,匹配的次数显然就是在可以匹配的区间中,每个前缀的出现次数之和. 首先是空前缀的出现次数,意味着你 ...
- 【BZOJ3413】匹配 离线+后缀树+树状数组
[BZOJ3413]匹配 Description Input 第一行包含一个整数n(≤100000). 第二行是长度为n的由0到9组成的字符串. 第三行是一个整数m. 接下来m≤5·10行,第i行是一 ...
- BZOJ3413 : 匹配
FDUSC前刷刷题吧.. 本题每个询问就是说将询问串与主串每个后缀匹配,若匹配成功则结束,否则加上lcp的长度 对主串建立后缀树,并用主席树维护DFS序 对于每个询问串,找到最后走到的点fin_nod ...
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- BZOJ3413: 匹配(后缀自动机,Parent树,线段树合并)
Description Input 第一行包含一个整数n(≤100000). 第二行是长度为n的由0到9组成的字符串. 第三行是一个整数m. 接下来m≤5·10行,第i行是一个由0到9组成的字符串s, ...
随机推荐
- 模仿开发H5游戏,看你有多色
开发记录 前言 之前跟着慕课网学习开发H5小游戏开心鱼,勾起我的兴趣. 在写代码的过程中,不怎么会遇到问题.虽然代码是亲手敲出来的,但是由于并没有对游戏的整体思路,所以并不知道开发与优化的过程. 为了 ...
- JavaScript string array 数组
Array类可以如下定义: var aValues = new Array(); 如果预先知道数组的长度,可以用参数传递长度 var aValues = new Array(20); -------- ...
- centos 虚拟机安装过程
centos装过好几次了,也装过好几次fedora,感觉centos更灵活些,这次我装了最简洁的centos,然后通过yum命令安装了各种需要的命令和软件,编译了phpredis.redis.和php ...
- POJ 3723 Conscription 最小生成树
题目链接: 题目 Conscription Time Limit: 1000MS Memory Limit: 65536K 问题描述 Windy has a country, and he wants ...
- DIV+CSS高手必知的15个CSS常识
1.不要使用过小的图片做背景平铺.这就是为何很多人都不用 1px 的原因,这才知晓.宽高 1px 的图片平铺出一个宽高 200px 的区域,需要 200*200=40, 000 次,占用资源. 2.无 ...
- Bootstrap 基础
一种前端开发框架,如同YUI 下载源码找开后,其文件结构如下: bootstrap/├── css/│ ├── bootstrap.css│ ├── bootstrap.min.css│ ...
- GetSurfaceLevel
if( SUCCEEDED( g_pTexture->GetSurfaceLevel( 0, &pSurface) ) ) { pd3dDevice->Se ...
- 指针强转和void*
C语言中,任何一个变量都必须占有一个地址,而这个地址空间内的0-1代码就是这个变量的值.不同的数据类型占有的空间大小不一,但是他们都必须有个地址,而这个地址就是硬件访问的依据,而名字只是提供给程序员的 ...
- [原] GLES在iOS和Android上的不同
本来GLES提供了与native platform的接口 EGL, 然而iOS没有使用EGL接口, 而是自己搞了一套,叫做EAGL的类似东西, 虽然说大同小异,但是在做跨平台的时候还是很恶心. elg ...
- PHP之session相关实例教程与经典代码
·php 中cookie和session的用法比较 ·phpmyadmin报错:Cannot start session without errors问题 ·php中cookie与session应用学 ...