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. eclipse集成testng插件(离线安装方式)

    testng是一个优秀的测试框架,我们在开发自动化测试脚本或者框架的时候经常会用到这个框架,因为它不仅能方便的帮助我们管理测试类,而且它还提供了丰富的注解来支持各种测试场景的实现(参数化,数据提供者, ...

  2. realstudio 粒子特效问题总结

    ParticleEmitter._inner_material.flags.depth_write = true;ParticleEmitter._inner_material.flags.depth ...

  3. 自然语言处理 - 如何通俗地理解TFIDF?

    本博客属个人学习笔记,如有疏漏,欢迎在评论留言指出~转载请注明. 在自然语言处理中,TFIDF常常被人提及.很多材料都提及TFIDF中的“普遍重要性”,但很少有材料去简单解释其中的原理.TFIDF其实 ...

  4. 团队介绍&学长采访

    1. 团队介绍 刘畅 博客园ID:森高Slontia 身份:PM 个人介绍: 弹丸粉 || 小说创作爱好者 || 撸猫狂魔 我绝对不知道,我一个写代码的怎么就当PM去了? 张安澜 博客园ID:Mins ...

  5. Yogurt factory

    Description The cows have purchased a yogurt factory that makes world-famous Yucky Yogurt. Over the ...

  6. 20162328蔡文琛 week06

    20162328 2017-2018-1 <程序设计与数据结构>第6周学习总结 教材学习内容总结 队列元素按FIFO的方式处理----最先进入的元素最先离开. 队列是保存重复编码k值得一种 ...

  7. C++:const用法的简单总结

    一.对变量的修饰 在c++中,如果我们希望定义一个值不会被改变的变量,那么可以用关键字const对它进行修饰,被修饰后的变量其作用相当于一个常量 //这两种方式等价 2 语法1:const 类型名 变 ...

  8. 使用JSon实现三级联动

    JSon实现三级联动 我觉得我这个方法比较麻烦,但是目前技术还比较弱,所以先做个笔记自己理解.目前没有和后台交互,只是在前台页面实现了 jQuery和JSon数据实现的,代码如下: <!DOCT ...

  9. P4语法(4)Control block

    Control block Control block之中用于放置设计好的Table和Action. 可以把control block认为是pipeline的一个模板,之前用的v1model中就是in ...

  10. 软工实践-Alpha 冲刺 (8/10)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 已经解决登录注册等基本功能的界面. 完成非功能的主界面制作 ...