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, ...
随机推荐
- Careercup - Google面试题 - 4557716425015296
2014-05-03 21:57 题目链接 原题: Many sticks with length, every time combine two, the cost is the sum of tw ...
- android中的selector背景选择器的用法
关于listview和button都要改变android原来控件的背景,在网上查找了一些资料不是很全,所以现在总结一下android的selector的用法. 首先android的selector是在 ...
- linux消息队列的使用
消息队列 *消息队列是内核地址空间中的内部链表,通过内核在各个进程之间传递的内容.消息顺序发送到消息队列中,每个消息队列都有IPC标识符唯一地进行标识. msgbuf结构 struct msgbuf{ ...
- ExtJs3带条件的分页查询的实现
使用ExtJs的同志们一定知道GridPanel哈~神器一般,非常方便的显示表格类型的数据,例如神马用户列表.产品列表.销售单列表.XXXX列表等.从数据库中查询所需的数据,以列表的形式显示出来,我们 ...
- linux文件系统创建文件的过程
创建一个文件最主要的步骤就是: 1.为文件创建一个文件目录项. 2.为文件创建一个inode结构并分配inode号,将inode编号与文件名映射关系保存在1中分配的文件目录项中. 3.将1中创建的文件 ...
- sql2008安装时提示重启计算机失败解决方法
安装sql 2008的时候,在检测安装环境中有一项是”重新启动计算机”显示的结果是“失败”.上网看了半天,找到了解决方案,虽然我没弄明白具体原因,但问题是解决了,解决方案如下: 一.Windows+R ...
- uva 11627
二分 #include <cstdio> #include <cstdlib> #include <cmath> #include <map> #inc ...
- Error: Exception in thread “main” java.lang.NoClassDefFoundError错误
Error: Exception in thread “main” java.lang.NoClassDefFoundError错误 检查文件名与类名是否一致 检查程序中main方法写的是否正确: p ...
- 解决asp.net mvc中*.resx资源文件访问报错
个人笔记 问题重现 在asp.net mvc中,使用资源文件会出现一个问题,例如: 紧接着我进入视图界面,输入下面代码: <a href="javascript:void(0);&qu ...
- the-type-java-lang-charsequence-cannot-be-resolved-in-package-declaration
http://stackoverflow.com/questions/24301986/the-type-java-lang-charsequence-cannot-be-resolved-in-pa ...