Portal -->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】相似子串的更多相关文章

  1. BZOJ3230 相似子串[后缀数组+二分+st表]

    BZOJ3230 相似子串 给一个串,查询排名i和j的子串longest common suffix和longest common prefix 思路其实还是蛮好想的,就是码起来有点恶心.可以发现后缀 ...

  2. BZOJ3230 相似子串 字符串 SA ST表

    原文链接http://www.cnblogs.com/zhouzhendong/p/9033092.html 题目传送门 - BZOJ3230 题意 给定字符串$s$.长度为$n$. 现在有$Q$组询 ...

  3. BZOJ3230: 相似子串

    3230: 相似子串 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 913  Solved: 223[Submit][Status]Descripti ...

  4. BZOJ3230 相似子串 【后缀数组】

    题目分析: 容易想到sa排好序之后,子串排名就是前面的子串减去height数组.所以正着做一遍,倒着做一遍就行了. 代码: #include<bits/stdc++.h> using na ...

  5. BZOJ3230: 相似子串【后缀数组】

    Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数. 第2行是字符串S. 接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出共Q行,每行一个数表示每组 ...

  6. [BZOJ3230]相似子串(后缀数组)

    显然可以通过后缀数组快速找到询问的两个串分别是什么,然后正反各建一个后缀数组来求两个串的LCP和LCS即可. #include<cstdio> #include<cstring> ...

  7. 2018.11.30 bzoj3230: 相似子串(后缀数组)

    传送门 后缀数组入门题. 建立正反两个后缀数组算就行了. 代码: #include<bits/stdc++.h> #define ri register int using namespa ...

  8. bzoj3796(后缀数组)(SA四连)

    bzoj3796Mushroom追妹纸 题目描述 Mushroom最近看上了一个漂亮妹纸.他选择一种非常经典的手段来表达自己的心意——写情书.考虑到自己的表达能力,Mushroom决定不手写情书.他从 ...

  9. 【BZOJ3230】相似子串 后缀数组+二分+RMQ

    [BZOJ3230]相似子串 Description Input 输入第1行,包含3个整数N,Q.Q代表询问组数.第2行是字符串S.接下来Q行,每行两个整数i和j.(1≤i≤j). Output 输出 ...

随机推荐

  1. 创建第一个Scrapy项目

    d:进入D盘 scrapy startproject tutorial建立一个新的Scrapy项目 工程的目录结构: tutorial/ scrapy.cfg # 部署配置文件 tutorial/ # ...

  2. AtCoder Regular Contest 101 D - Median of Medians

    二分答案 然后前缀和+树状数组来判断这个答案是否大于等于数 如果我们对于一个查询,如果小于这个数令为1,大于这个数领为-1 将所有前缀和放在树状数组中,就可以查询所有sum_{l} < sum_ ...

  3. SteamVR Unity Plugin - v2.0.1中的InteractionSystem

    最近写VR项目的时候用到了SteamVR Unity Plugin - v2.0.1插件,感觉比之前用到的SteamVR plugin for Unity - v1.2.2版本改进了很多,就算不用VR ...

  4. div不设置高度背景颜色或外边框不能显示的解决方法

    在使用div+css进行网页布局时,如果外部div有背景颜色或者边框,而不设置其高度,在浏览时出现最外层Div的背景颜色和边框不起作用的问题. 大体结构<div class="oute ...

  5. PKI(Public Key Infrastucture)介绍

    PKI(Public Key Infrastucture)介绍 根据Wikipedia PKI词条整理. PKI(Public Key Infrastucture)是一系列的规则.策略以及过程,可以用 ...

  6. python3【基础】-list&tuple

    一.list概述 list (列表)是python中最常用的数据类型之一,通过列表可以对数据实现最方便的存储,修改等操作.在python3中,list支持如下方法: Help on class lis ...

  7. redis 常用命令 结合php

    这篇文章主要介绍了30个php操作redis常用方法代码例子,本文其实不止30个方法,可以操作string类型.list类型和set类型的数据,需要的朋友可以参考下     redis的操作很多的,以 ...

  8. 模拟登入教务处(header)

    import HTMLParser import urlparse import urllib import urllib2 import cookielib import string import ...

  9. HDU 5187 zhx's contest 快速幂,快速加

    题目链接: hdu: http://acm.hdu.edu.cn/showproblem.php?pid=5187 bc(中文): http://bestcoder.hdu.edu.cn/contes ...

  10. 在.net项目中使用Consul

    1.创建.net core web程序并运行 2.在Consul中注册该服务 Consul支持两种服务注册的方式,一种是通过Consul的服务注册HTTP API,由服务自身在启动后调用API注册自己 ...