bzoj3230
以前觉得这题好难,现在觉得这题还是挺简单
首先看到类似LCP问题不难想到后缀数组吧
前后的相似需要我们分别做一个后缀数组和“前缀数组”(就是把字符串反向然后跑后缀数组)
这道题的难点就在于如何确定子串是什么
考虑到一个有用的结论:任何一个子串都是某一个后缀的某一个前缀
由于做完后缀数组之后,后缀已经按照从小到大排列了
因此考虑相邻名次后缀sa[i]和sa[i-1],然后在这两个后缀间,还夹着一些子串
不难发现,这些子串就是后缀sa[i]从第h[i]+1位开始的前缀
为什么是从h[i]+1开始的前缀呢,因为前缀1~h[i]在一定已经在后缀sa[i-1]出现过了,要避免重复
因此,我们可以计算相邻两个后缀之间不同的子串数目,然后维护前缀和
这样就可以用二分来确定名词为x的子串是哪个后缀的前缀
然后求相似程度不难想到用ST解决 利用LCP(i,j)=min(height[rank[i]+1~rank[j]]) 假定rank[i]<rank[j]
注意这道题名次可能会爆longint,我一开始都想到子串数目会爆longint,可读入的时候竟然忘了用int64,囧……~
var sa,rank,h:array[..,..] of longint;
f:array[..,..,..] of longint;
x,y:array[..] of longint;
d:array[..] of longint;
sum:array[..] of int64;
s1,s2:ansistring;
i,n,t:longint;
p,q:int64; function min(a,b:int64):int64;
begin
if a>b then exit(b) else exit(a);
end; procedure swap(var a,b:longint);
var c:longint;
begin
c:=a;
a:=b;
b:=c;
end; procedure suffix(s:ansistring;k:longint);
var m,i,j:longint;
begin
fillchar(sum,sizeof(sum),);
for i:= to n do
begin
y[i]:=ord(s[i]);
inc(sum[y[i]]);
end;
for i:= to do
inc(sum[i],sum[i-]);
for i:=n downto do
begin
sa[k,sum[y[i]]]:=i;
dec(sum[y[i]]);
end;
p:=;
rank[k,sa[k,]]:=;
for i:= to n do
begin
if (y[sa[k,i]]<>y[sa[k,i-]]) then inc(p);
rank[k,sa[k,i]]:=p;
end;
m:=p;
j:=;
while m<n do
begin
y:=rank[k];
fillchar(sum,sizeof(sum),);
p:=;
for i:=n-j+ to n do
begin
inc(p);
x[p]:=i;
end;
for i:= to n do
if sa[k,i]>j then
begin
inc(p);
x[p]:=sa[k,i]-j;
end; for i:= to n do
begin
rank[k,i]:=y[x[i]];
inc(sum[rank[k,i]]);
end;
for i:= to m do
inc(sum[i],sum[i-]);
for i:=n downto do
begin
sa[k,sum[rank[k,i]]]:=x[i];
dec(sum[rank[k,i]]);
end;
p:=;
rank[k,sa[k,]]:=;
for i:= to n do
begin
if (y[sa[k,i]]<>y[sa[k,i-]]) or (y[sa[k,i]+j]<>y[sa[k,i-]+j]) then inc(p);
rank[k,sa[k,i]]:=p;
end;
m:=p;
j:=j shl ;
end;
h[k,]:=;
p:=;
for i:= to n do
begin
if rank[k,i]= then continue;
j:=sa[k,rank[k,i]-];
while (i+p<=n) and (j+p<=n) and (s[i+p]=s[j+p]) do inc(p);
h[k,rank[k,i]]:=p;
if p> then dec(p);
end;
end; procedure rmq(k:longint);
var t,i,j:longint;
begin
for i:= to n do
f[k,i,]:=h[k,i];
t:=trunc(ln(n)/ln());
for j:= to t do
for i:= to n do
if (i+d[j]-<=n) then
f[k,i,j]:=min(f[k,i,j-],f[k,i+d[j-],j-])
else break;
end; function ask(x,y,k:longint):longint;
var t:longint;
begin
if x=y then exit(n+-sa[k,x]);
if x>y then swap(x,y);
inc(x);
t:=trunc(ln(y-x+)/ln());
exit(min(f[k,x,t],f[k,y-d[t]+,t]));
end; function find(x:int64):longint;
var l,m,r:longint;
begin
l:=;
r:=n;
while l<=r do
begin
m:=(l+r) shr ;
if (sum[m-]<x) and (sum[m]>=x) then exit(m);
if sum[m]<x then l:=m+ else r:=m-;
end;
end; function getans(x,y:int64):int64;
var a,b,aa,bb,c:longint;
begin
a:=find(x); //查找是哪个后缀的前缀
b:=find(y);
aa:=sa[,a]+h[,a]+x-sum[a-]-; //定位前缀数组中的位置
bb:=sa[,b]+h[,b]+y-sum[b-]-;
aa:=rank[,n+-aa];
bb:=rank[,n+-bb];
c:=min(h[,a]+x-sum[a-],h[,b]+y-sum[b-]); //注意这里我们是求子串所在后缀的LCP,可能会超出原来子串的长度
getans:=sqr(min(c,ask(a,b,)))+sqr(min(c,ask(aa,bb,)));
end; begin
readln(n,t);
readln(s1);
s2:='';
for i:=n downto do
s2:=s2+s1[i];
suffix(s1,);
suffix(s2,);
d[]:=;
for i:= to trunc(ln(n)/ln()) do
d[i]:=d[i-]*;
rmq(); //处理ST
rmq();
sum[]:=;
for i:= to n do
sum[i]:=sum[i-]+n+-sa[,i]-h[,i];
for i:= to t do
begin
readln(p,q);
if (p>sum[n]) or (q>sum[n]) then //判断是否超出子串数目
begin
writeln(-);
continue;
end;
writeln(getans(p,q));
end;
end.
bzoj3230的更多相关文章
- BZOJ3230 相似子串 字符串 SA ST表
原文链接http://www.cnblogs.com/zhouzhendong/p/9033092.html 题目传送门 - BZOJ3230 题意 给定字符串$s$.长度为$n$. 现在有$Q$组询 ...
- 【bzoj3230】相似子串
Portal -->bzoj3230 Description 给你一个长度为\(n\)的字符串,把它的所有本质不同的子串按字典序大小排序,有\(m\)个询问,对于每一个询问\(x,y\)你需要回 ...
- 【BZOJ3230】相似子串 后缀数组+二分+RMQ
[BZOJ3230]相似子串 Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数.第2行是字符串S.接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出 ...
- BZOJ3230 相似子串[后缀数组+二分+st表]
BZOJ3230 相似子串 给一个串,查询排名i和j的子串longest common suffix和longest common prefix 思路其实还是蛮好想的,就是码起来有点恶心.可以发现后缀 ...
- [BZOJ3230]相似子串(后缀数组)
显然可以通过后缀数组快速找到询问的两个串分别是什么,然后正反各建一个后缀数组来求两个串的LCP和LCS即可. #include<cstdio> #include<cstring> ...
- BZOJ3230: 相似子串
3230: 相似子串 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 913 Solved: 223[Submit][Status]Descripti ...
- BZOJ3230 相似子串 【后缀数组】
题目分析: 容易想到sa排好序之后,子串排名就是前面的子串减去height数组.所以正着做一遍,倒着做一遍就行了. 代码: #include<bits/stdc++.h> using na ...
- 2018.11.30 bzoj3230: 相似子串(后缀数组)
传送门 后缀数组入门题. 建立正反两个后缀数组算就行了. 代码: #include<bits/stdc++.h> #define ri register int using namespa ...
- BZOJ3230: 相似子串【后缀数组】
Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数. 第2行是字符串S. 接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出共Q行,每行一个数表示每组 ...
随机推荐
- svn is alread locked
右键文件夹team →Cleanup就可以了 is not under version control team→ clean 等等更新
- c++中返回对象与返回引用的区别
这几天在做用C++做课程设计,对其返回对象的实现感到迷惑. 通过对汇编代码的分析,可以清楚的看到,直接返回引用和返回对象的区别到底是什么. 分析的程序如下 #include<cstdio> ...
- [转] 一个资深iOS开发者对于React Native的看法
当我第一次尝试ReactNative的时候,我觉得这只是网页开发者涉足原生移动应用领域的歪门邪道. 我认为一个js开发者可以使用javascript来构建iPhone应用确实是一件很酷的事情,但是我很 ...
- PHP利用超级全局变量$_POST来接收表单数据。
利用$_POST超级全局变量接收表单的数据,然后利用echo输出到页面. 下面是代码: <!doctype html> <html> <head> <titl ...
- 内存储与外存储(IO流的应用)
存储分为内存储,外存储(Sd卡存储),其中还涉及IO流的应用. 我们先来看内存储,在布局中EditView中输入的内容,通过点击按钮,分别进行保存,读取,删除的操作,读取时显示在下面的TextView ...
- 在Iframe框架下如何跳转到登录界面
在Iframe框架下跳转到登录界面总会跳到子界面中,类似于下图 试用Respon.Redirect()不行, 用Js函数,但我跳转代码都是写在cs文件中的,用Respose.write(),js函数根 ...
- Android学习笔记(广播机制)
1.Android的广播机制介绍 收听收音机也是一种广播,在收音机中有很多个广播电台,每个广播电台播放的内容都不相同.接受广播时广播(发送方)并不在意我们(接收方)接收到广播时如何处理.好比我们收听交 ...
- Sprite Kit教程:初学者
作者:Ray Wenderlich 原文出处:点击打开链接 http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners 转自 ...
- 初探grunt.js
package.js { "name": "ttd_v3", "version": "0.1.0", "aut ...
- C# RSA
using System; using System.Security.Cryptography; using System.Text; class RSACSPSample { static voi ...