题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230

作出后缀数组,从 LCP 看每个位置对于本质不同子串的贡献,而且他们已经按前面部分排好序了,所以直接在 sa[ ] 上二分就能找到询问的子串。

找最长公共前缀就用 ht[ ] 和子串的长度比较就行。找最长公共后缀就一开始把原串翻转,做出翻转后的 ht[ ] ,就能查询了。

也可以二分一个最长公共后缀的位置,然后用正常的 ht[ ] 判断。

注意 long long 。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+,K=;
int n,sa[][N],rk[][N],tp[N],tx[N];ll sm[N];
int ht[][N][K],bin[K],lg[N];
char s[N];
struct Node{int ps,len;};
int Mn(int a,int b){return a<b?a:b;}
int rdn()
{
int ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
ll rdl()
{
ll ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
void init()
{
lg[]=;for(int i=;i<=n;i++)lg[i]=lg[i>>]+;
bin[]=;for(int i=;i<=lg[n];i++)bin[i]=bin[i-]<<;
}
void Rsort(int n,int nm,int x)
{
for(int i=;i<=nm;i++)tx[i]=;
for(int i=;i<=n;i++)tx[rk[x][i]]++;
for(int i=;i<=nm;i++)tx[i]+=tx[i-];
for(int i=n;i;i--)sa[x][tx[rk[x][tp[i]]]--]=tp[i];
}
void get_sa(int n,int x)
{
int nm=;
for(int i=;i<=n;i++)tp[i]=i,rk[x][i]=s[i]-'a'+;
Rsort(n,nm,x);
for(int k=;k<=n;k<<=)
{
int tot=;
for(int i=n-k+;i<=n;i++)tp[++tot]=i;
for(int i=;i<=n;i++)
if(sa[x][i]>k)tp[++tot]=sa[x][i]-k;
Rsort(n,nm,x);memcpy(tp,rk[x],sizeof rk[x]);
nm=;rk[x][sa[x][]]=;
for(int i=,u,v;i<=n;i++)
{
u=sa[x][i]+k;v=sa[x][i-]+k;if(u>n)u=;if(v>n)v=;
rk[x][sa[x][i]]=(tp[sa[x][i]]==tp[sa[x][i-]]&&tp[u]==tp[v])?nm:++nm;
}
if(nm==n)break;
}
}
void get_ht(int n,int x)
{
for(int i=,k=,j;i<=n;i++)
{
for(k?k--:,j=sa[x][rk[x][i]-];i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++);
ht[x][rk[x][i]][]=k;
}
for(int j=;j<=lg[n];j++)
for(int i=;i+bin[j]-<=n;i++)
ht[x][i][j]=Mn(ht[x][i][j-],ht[x][i+bin[j-]][j-]);
}
int get_lcp(int l,int r,int x)
{
if(l==r)return n-l+;
l=rk[x][l]; r=rk[x][r]; if(l>r)swap(l,r);
int d=lg[r-l];
return Mn(ht[x][l+][d],ht[x][r-bin[d]+][d]);
}
Node fnd(ll x)
{
int l=,r=n,ret=;
while(l<=r)
{
int mid=l+r>>;
if(sm[mid]>=x)ret=mid,r=mid-;
else l=mid+;
}
if(!ret)return (Node){,};
int d=n-sa[][ret]+-ht[][ret][];
d=ht[][ret][]+(x-sm[ret-]);
return (Node){sa[][ret],d};
}
int main()
{
n=rdn();int Q=rdn();scanf("%s",s+);init();
get_sa(n,);get_ht(n,);
reverse(s+,s+n+);get_sa(n,);get_ht(n,);
for(int i=;i<=n;i++)
sm[i]=sm[i-]+(n-sa[][i]+)-ht[][i][];
ll x,y; Node a,b;//long long!!!
while(Q--)
{
x=rdl(); y=rdl();//long long!!
a=fnd(x); b=fnd(y);
if(!a.ps||!b.ps){puts("-1");continue;}
x=Mn(get_lcp(a.ps,b.ps,),Mn(a.len,b.len));
y=Mn(get_lcp(n-(a.ps+a.len-)+,n-(b.ps+b.len-)+,),Mn(a.len,b.len));
printf("%lld\n",x*x+y*y);
}
return ;
}

bzoj 3230 相似子串——后缀数组的更多相关文章

  1. BZOJ 3230 相似子串 | 后缀数组 二分 ST表

    BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过 ...

  2. BZOJ 3230: 相似子串(后缀数组)

    传送门 解题思路 其实题目挺好想的.首先子串排名可以由后缀数组求得,因为不算重复的,所以后缀数组的每个后缀排名的去掉\(lcp\)的前缀排名为当前后缀的子串排名.这样就可以预处理出每个后缀的\(l,r ...

  3. BZOJ 3230 相似子串 ——后缀数组

    题目的Source好有趣. 我们求出SA,然后求出每一个后缀中与前面本质不同的字符串的个数. 然后二分求出当前的字符串. 然后就是正反两次后缀数组求LCP的裸题了. 要注意,这时两个串的起点可能会相同 ...

  4. bzoj 3230 相似子串 —— 后缀数组+二分

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3230 先算出每个后缀贡献子串的区间: 然后前缀LCP直接查询,后缀LCP二分长度,查询即可: ...

  5. BZOJ 1396: 识别子串( 后缀数组 + 线段树 )

    这道题各位大神好像都是用后缀自动机做的?.....蒟蒻就秀秀智商写一写后缀数组解法..... 求出Height数组后, 我们枚举每一位当做子串的开头. 如上图(x, y是height值), Heigh ...

  6. poj 2774 最长公共子串 后缀数组

    Long Long Message Time Limit: 4000MS   Memory Limit: 131072K Total Submissions: 25752   Accepted: 10 ...

  7. URAL 1297 最长回文子串(后缀数组)

    1297. Palindrome Time limit: 1.0 secondMemory limit: 64 MB The “U.S. Robots” HQ has just received a ...

  8. poj 1743 Musical Theme(最长重复子串 后缀数组)

    poj 1743 Musical Theme(最长重复子串 后缀数组) 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复 ...

  9. BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

    二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) -------------------------------------------------- ...

随机推荐

  1. Spring AOP(3)

  2. [BZOJ2017][Usaco2009 Nov]硬币游戏

    Description 农夫约翰的奶牛喜欢玩硬币游戏,因此他发明了一种称为“Xoinc”的两人硬币游戏. 初始时,一个有N(5 <= N <= 2,000)枚硬币的堆栈放在地上,从堆顶数起 ...

  3. 通过代码来调用log4net写日志

    1.使用如下配置 http://www.cnblogs.com/chucklu/p/5404813.html 2.调用 string ConfigFile; private void LoadLogC ...

  4. Caffe2——C++ 预测(predict)Demo

    因为最近入坑Caffe2,它最近还一直在更新,所以坑比较多,官方也只给出了python的demo,C++的暂时还找不到,有也只有一个简单版的,不够用,所以就总结了一下,结合网上和自己的实践,整理了一下 ...

  5. springBean的作用域

    Bean的作用域有五个类别 1.singleton,不写的话默认也是这个,这个的意思就是,单例的,就是说,不管你new多少次,都是一个对象 2.prototype,就是说每次new一个bean都是一个 ...

  6. nginx 403错误

    1.出现此种错误的原因有可能是所有者对目录没有写的权限,此时可用chmod 777 目录名 先完全放开权限,如果问题解决,则在慢慢缩小访问权限. 解决办法:chown -R nginx_user:ng ...

  7. Python连接MongoDB操作

    1.安装PyMongo 注意:请勿安装“bson”软件包. PyMongo配有自己的bson包; 执行“pip install bson”或“easy_install bson”则会安装与PyMong ...

  8. CentOS下安装mysql及配置使用

    最近一直使用的是CentOS,平时用的最多的数据库是Sql Server,对于mysql还停留在上学的时候,早已忘得一干二净,写这篇内容目的是,重新学习如何安装使用mysql. 一.安装mysql 操 ...

  9. Codeforces Round #181 (Div. 2)C

    用lucas定理, p必须是素数 对于单独的C(n, m) mod p,已知C(n, m) mod p = n!/(m!(n - m)!) mod p.显然除法取模,这里要用到m!(n-m)!的逆元. ...

  10. Linux命令2018-03-01更新

    前言:Linux主要应用于服务器端,嵌入式开发和个人pc桌面端 本人wechat:YWNlODAyMzU5MTEzMTQ=. *** GPL GPL是一个开源许可协议,由自由软件基金会创建的.GPL许 ...