【bzoj3230】相似子串
Description
给你一个长度为\(n\)的字符串,把它的所有本质不同的子串按字典序大小排序,有\(m\)个询问,对于每一个询问\(x,y\)你需要回答排名\(x\)和子串和排名\(y\)的子串的最长公共前缀的平方和+最长公共后缀的平方和,如果询问不合法输出\(-1\)
数据范围:\(n<=10^5,m<=10^5\),字符串只含有小写字母
Solution
虽然说貌似是。。后缀数组的裸题。。
但是场上没有写出来。。所以还是拎出来智力康复。。
假设我们已经求出来\(sa\)数组和\(height\)数组,怎么来做这个题呢
首先需要判断询问合不合法,即要求出本质不同的子串的个数
这里可以直接用\(height\)数组求解,以样例为例子,放个图就比较好理解了:

简单一点来说就是非公共前缀的部分才能贡献新的本质不同的子串
所以我们稍微减一下就能够知道每一个后缀贡献了几个新的子串了,前缀和一下,把这个存前缀和的数组记为\(num\),则\(num[i]\)表示排名为\(1\)到\(i\)的后缀总共贡献了几个子串
那么总的本质不同的子串的数量就是\(num[n]\)了
接下来就是求最长公共前缀
如果知道了两个子串具体是啥当然很好做啦,只要子串的开头对应的两个后缀RMQ一下然后跟两个子串的长度取个\(min\)就好了
然而这里的主要问题出在我们要定位,但是因为我们有了\(num\)数组,所以直接二分一下就可以知道是由哪个后缀产生的了(找到第一个\(num\)大于等于的即可)
最后是后缀
后缀的话其实就是反串的前缀,处理方式相同,但是定位的话我们可以直接根据求最长公共前缀的时候定位得到的信息,反转一下就能知道在反串中是由哪个后缀产生的了
最后的话是。。输出的时候和子串数量的统计都需要long long
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define TOP 20
using namespace std;
const int N=100010;
char s[N];
int n,m,lens;
struct Sa{/*{{{*/
int a[N],b[N],c[N];
int rk[N],height[N],sa[N],mn[N][TOP+1];
ll num[N];
int mx,n;
bool cmp(int x,int y,int len,int *r)
{return r[x]==r[y]&&r[x+len]==r[y+len];}
void sort(int n){
for (int i=0;i<=mx;++i) c[i]=0;
for (int i=1;i<=n;++i) ++c[a[b[i]]];
for (int i=1;i<=mx;++i) c[i]+=c[i-1];
for (int i=n;i>=1;--i) sa[c[a[b[i]]]--]=b[i];
}
void get_sa(int _n){
mx=0; n=_n;
for (int i=1;i<=n;++i) b[i]=i,a[i]=s[i]-'a'+1,mx=max(mx,a[i]);
int cnt=0;
sort(n);
for (int len=1;cnt<n;len*=2){
cnt=0;
for (int i=n-len+1;i<=n;++i) b[++cnt]=i;
for (int i=1;i<=n;++i)
if (sa[i]>len)
b[++cnt]=sa[i]-len;
sort(n);
swap(a,b);
cnt=1;
a[sa[1]]=1;
for (int i=2;i<=n;a[sa[i++]]=cnt)
if (!cmp(sa[i],sa[i-1],len,b))
++cnt;
mx=cnt;
}
}
void get_height(){
for (int i=1;i<=n;++i) rk[sa[i]]=i;
int k=0;
for (int i=1;i<=n;++i){
if (k>0) --k;
while (s[i+k]==s[sa[rk[i]-1]+k]) ++k;
height[rk[i]]=k;
}
cnt_num();
rmq();
}
void cnt_num(){
for (int i=1;i<=n;++i)
num[i]=num[i-1]+(n-sa[i]+1-height[i]);
}
void rmq(){
for (int i=1;i<=n;++i) mn[i][0]=height[i];
for (int j=1;j<=TOP;++j)
for (int i=n-(1<<j)+1;i>=1;--i)
mn[i][j]=min(mn[i][j-1],mn[i+(1<<j-1)][j-1]);
}
int lcp(int x,int y){//x&y are ranks
if (x==y) return n-sa[x]+1;
if (x>y) swap(x,y);
++x;
int tmp=(int)(log(1.0*(y-x+1))/log(2.0));
return min(mn[x][tmp],mn[y-(1<<tmp)+1][tmp]);
}
int query(ll x,ll y,int &edx,int &edy,int &lenx,int &leny){
int posx,posy;
posx=lower_bound(num+1,num+1+n,x)-num;
posy=lower_bound(num+1,num+1+n,y)-num;
int ret=lcp(posx,posy);
lenx=n-sa[posx]+1-(num[posx]-x);
leny=n-sa[posy]+1-(num[posy]-y);
edx=n-sa[posx]+1-lenx+1;
edy=n-sa[posy]+1-leny+1;
return min(ret,min(lenx,leny));
}
int query2(int stx,int sty,int lenx,int leny){
int ret=lcp(rk[stx],rk[sty]);
return min(ret,min(lenx,leny));
}
}a[2];/*}}}*/
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int tmp1,tmp2,edx,edy,lenx,leny;
ll x,y;
scanf("%d%d",&n,&m);
scanf("%s",s+1);
lens=strlen(s+1);
a[0].get_sa(lens); a[0].get_height();
for (int i=1;lens-i+1>i;++i) swap(s[i],s[lens-i+1]);
a[1].get_sa(lens); a[1].get_height();
for (int i=1;i<=m;++i){
scanf("%lld%lld",&x,&y);
if (x>a[0].num[n]||y>a[0].num[n]) printf("-1\n");
else{
tmp1=a[0].query(x,y,edx,edy,lenx,leny);
tmp2=a[1].query2(edx,edy,lenx,leny);
printf("%lld\n",1LL*tmp1*tmp1+1LL*tmp2*tmp2);
}
}
}
【bzoj3230】相似子串的更多相关文章
- BZOJ3230 相似子串[后缀数组+二分+st表]
BZOJ3230 相似子串 给一个串,查询排名i和j的子串longest common suffix和longest common prefix 思路其实还是蛮好想的,就是码起来有点恶心.可以发现后缀 ...
- BZOJ3230 相似子串 字符串 SA ST表
原文链接http://www.cnblogs.com/zhouzhendong/p/9033092.html 题目传送门 - BZOJ3230 题意 给定字符串$s$.长度为$n$. 现在有$Q$组询 ...
- 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 ...
- BZOJ3230: 相似子串【后缀数组】
Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数. 第2行是字符串S. 接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出共Q行,每行一个数表示每组 ...
- [BZOJ3230]相似子串(后缀数组)
显然可以通过后缀数组快速找到询问的两个串分别是什么,然后正反各建一个后缀数组来求两个串的LCP和LCS即可. #include<cstdio> #include<cstring> ...
- 2018.11.30 bzoj3230: 相似子串(后缀数组)
传送门 后缀数组入门题. 建立正反两个后缀数组算就行了. 代码: #include<bits/stdc++.h> #define ri register int using namespa ...
- bzoj3796(后缀数组)(SA四连)
bzoj3796Mushroom追妹纸 题目描述 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从 ...
- 【BZOJ3230】相似子串 后缀数组+二分+RMQ
[BZOJ3230]相似子串 Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数.第2行是字符串S.接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出 ...
随机推荐
- 【Jmeter测试】使用Java请求进行Dubbo接口的测试
如何构建一个Dubbo接口测试的通用框架(https://github.com/nitibu/jmeter-dubbo-test)从上面的流程我们可以看出,测试类大致的一个结构: 使用json文件来 ...
- 420. Count and Say【LintCode java】
Description The count-and-say sequence is the sequence of integers beginning as follows: 1, 11, 21, ...
- PRML学习笔记第一章
[转] PRML笔记 - 1.1介绍 模式识别的目标 自动从数据中发现潜在规律,以利用这些规律做后续操作,如数据分类等. 模型选择和参数调节 类似的一族规律通常可以以一种模型的形式为表达,选择合适模型 ...
- 第五章—if语句
5-1 条件测试 :编写一系列条件测试:将每个测试以及你对其结果的预测和实际结果都打印出来.你编写的代码应类似于下面这样: car = 'subaru' print("Is car == ' ...
- 关于cisco路由器配置的一些参数
单臂路由设置 Switch(config-if)#no switchport Switch(config)#ip routingSwitch(config)#interface FastEtherne ...
- Blockchain For Dummies(IBM Limited Edition
Blockchain For Dummies(IBM Limited Edition)笔记 该系列内容主要介绍用于商业的区块链,有人说区块链之于贸易,犹如因特网之于信息.在商业领域区块链可以用于交易任 ...
- 博弈---尼姆博奕(Nimm Game)(重点)
尼姆博奕(Nimm Game):有三堆各若干个物品,两个人轮流从某一堆取任意多的 物品,规定每次至少取一个,多者不限,最后取光者得胜. 这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示 ...
- html个人网页
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- java 队列的使用(转载)
转载声明:http://blog.csdn.net/lzy_lizhiyang/article/details/48311925 先我们要知道使用队列的目的是什么?一般情况下,如果是一些及时消息的处理 ...
- rsyslog配置文件详解(rsyslog.conf)
# rsyslog configuration file # For more information see /usr/share/doc/rsyslog-*/rsyslog_conf.html # ...