正题


题目大意

给出一个长度为nnn的字符串,两个串相似当且仅当可以通过每种字符置换使得它们相同。

qqq次询问这个字符串所有子串中和这个串中sl,rs_{l,r}sl,r​子串有多少个相似的。

1≤n≤105,1≤q≤5×1051\leq n\leq 10^5,1\leq q\leq 5\times 10^51≤n≤105,1≤q≤5×105

字符集是数字0∼90\sim 90∼9


解题思路

请问我是在阴间吗

首先对于相似的比较相信很常见,维护每个数字上一个和它相同的数字的距离,然后没有上一个就定为000就好了。

但是这题的问题在于我们提取出区间构成的数组时前面有些要变成000。

同样的这也是个提示,因为字符集大小只有10,我们也可以从这里入手,对于一个后缀,我们把第一个出现的数字的位置挖空后,我们至多会把这个后缀以这些位置分成101010份,我们将这个字符串序列称之为这个后缀的值。

然后我们需要的就是这些后缀值的“LCP”,而这样我们需要我们能快速求这些后缀中字符串的LCP。

子串的LCP直接上SA+RMQ就好了。

这样我们把弄出来的后缀的值排好序,然后维护一个相邻的两两之间的"LCP"计入一个类似height的数组的东西。

然后对于询问我们就直接二分在RMQ上查询就好了。

时间复杂度:O(10nlog⁡n+qlog⁡n)O(10n\log n+q\log n)O(10nlogn+qlogn)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+10;
struct node{int l,r;
};
struct nstr{vector<node> r;int id;
}sr[N];
int n,m,q,nxt[10],p[10],pos[N];
int x[N],y[N],c[N],sa[N],rk[N];
int lg[N],f[N][20],h[N],s[N];
char rs[N];
void Qsort(){for(int i=1;i<=m;i++)c[i]=0;for(int i=1;i<=n;i++)c[x[i]]++;for(int i=1;i<=m;i++)c[i]+=c[i-1];for(int i=n;i>=1;i--)sa[c[x[y[i]]]--]=y[i],y[i]=0;return;
}
void Get_SA(){for(int i=1;i<=n;i++)x[i]=s[i]+1,m=max(m,s[i]+1),y[i]=i;Qsort();for(int w=1;w<=n;w<<=1){int p=0;for(int i=n-w+1;i<=n;i++)y[++p]=i;for(int i=1;i<=n;i++)if(sa[i]>w)y[++p]=sa[i]-w;Qsort();swap(x,y);x[sa[1]]=p=1;for(int i=2;i<=n;i++)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+w]==y[sa[i-1]+w])?p:(++p);if(p==n)break;m=p;}return;
}
void Get_Height(){int k=0;for(int i=1;i<=n;i++)rk[sa[i]]=i;for(int i=1;i<=n;i++){if(rk[i]==1)continue;if(k)k--;int j=sa[rk[i]-1];while(i+k<=n&&j+k<=n&&s[j+k]==s[i+k])k++;h[rk[i]]=f[rk[i]][0]=k;}return;
}
void Get_RMQ(){for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;for(int j=1;(1<<j)<=n;j++)for(int i=1;i+(1<<j)-1<=n;i++)f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);return;
}
int RMQ(int l,int r){if(!l||!r)return 0;if(l==r)return n-l+1;l=rk[l];r=rk[r];if(l>r)swap(l,r);l++;int z=lg[r-l+1];return min(f[l][z],f[r-(1<<z)+1][z]);
}
int RMQs(int l,int r){l++;int z=lg[r-l+1];return min(f[l][z],f[r-(1<<z)+1][z]);
}
void SA(){Get_SA();Get_Height();Get_RMQ();return;
}
int cp(node x,node y){//x<=yif(!x.l&&!y.l)return 2;if(!x.l)return 1;if(!y.l)return 0;int len=RMQ(x.l,y.l);if(len>x.r-x.l||len>y.r-y.l){if(x.r-x.l==y.r-y.l)return 2;return (x.r-x.l)<(y.r-y.l);}return s[x.l+len]<s[y.l+len];
}
bool cmp(nstr x,nstr y){int i=0;while(1){if(i>=x.r.size())return 0;if(i>=y.r.size())return 1;int op=cp(x.r[i],y.r[i]);if(op==2)i++;else return op;}return 0;
}
int LCP(nstr x,nstr y){int i=0,ans=0;while(i<x.r.size()&&i<y.r.size()&&cp(x.r[i],y.r[i])==2)ans+=x.r[i].r-x.r[i].l+1,i++;if(i<x.r.size()&&i<y.r.size())ans+=min(RMQ(x.r[i].l,y.r[i].l),min(x.r[i].r-x.r[i].l,y.r[i].r-y.r[i].l)+1);return ans;
}
int main()
{//  freopen("similar.in","r",stdin);
//  freopen("similar.out","w",stdout); scanf("%d%d",&n,&q);scanf("%s",rs+1);for(int i=1;i<=n;i++){if(!nxt[rs[i]-'0'])s[i]=0;else s[i]=i-nxt[rs[i]-'0'];nxt[rs[i]-'0']=i;}SA();memset(nxt,0,sizeof(nxt));for(int i=n;i>=1;i--){nxt[rs[i]-'0']=i;for(int j=0;j<=9;j++)p[j]=nxt[j];sort(p,p+10);int now=i;for(int j=0;j<=9;j++){if(!p[j])continue;if(p[j]>now)sr[i].r.push_back((node){now,p[j]-1});sr[i].r.push_back((node){0,0});now=p[j]+1;}if(now<=n)sr[i].r.push_back((node){now,n});sr[i].id=i;}sort(sr+1,sr+1+n,cmp);for(int i=1;i<=n;i++)pos[sr[i].id]=i;for(int i=2;i<=n;i++)h[i]=LCP(sr[i-1],sr[i]);for(int i=2;i<=n;i++)f[i][0]=h[i];for(int j=1;(1<<j)<=n;j++)for(int i=1;i+(1<<j)-1<=n;i++)f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);int las=0;while(q--){int l,r;scanf("%d%d",&l,&r);l^=las;r^=las;if(l>n||r>n||l<1||r<1)continue;int x=pos[l],len=r-l+1;int L=x+1,R=n,ans=1;while(L<=R){int mid=(L+R)>>1;if(RMQs(x,mid)>=len)L=mid+1;else R=mid-1;}ans+=R-x;L=1;R=x-1;while(L<=R){int mid=(L+R)>>1;if(RMQs(mid,x)>=len)R=mid-1;else L=mid+1;}ans+=x-L;printf("%d\n",las=ans);}return 0;
}

YbtOJ-相似子串【SA,RMQ,二分】相关推荐

  1. BZOJ 4892: [Tjoi2017]DNA(SA+RMQ / SAM)

    Description 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的 性状,但是研究人员发现对碱基序列S,任意修改其中不超过3个碱基,依然能够 ...

  2. 【bzoj2500】幸福的道路 树形dp+倍增RMQ+二分

    原文地址:http://www.cnblogs.com/GXZlegend/p/6825389.html 题目描述 小T与小L终于决定走在一起,他们不想浪费在一起的每一分每一秒,所以他们决定每天早上一 ...

  3. 【HDU - 5875】Function(线段树,区间第一个小于某个数的数 或 RMQ二分)

    题干: The shorter, the simpler. With this problem, you should be convinced of this truth.        You a ...

  4. [2020.11.26NOIP模拟赛]勇者的后缀【SA,RMQ,主席树,二分】

    正题 题目链接:https://www.luogu.com.cn/problem/U142356?contestId=37784 题目大意 一个字符串,询问给出(x,l,r)(x,l,r)(x,l,r ...

  5. BZOJ 4310 二分+SA+RMQ

    思路: 首先求出后缀数组和height数组,这样能得到本质不同的子串数目 这里利用:本质不同的子串=∑(Len−SA[i]−height[i])=∑(Len−SA[i]−height[i])利用SA[ ...

  6. YbtOJ#463-序列划分【二分答案,线段树,dp】

    正题 题目链接:https://www.ybtoj.com.cn/problem/463 题目大意 给出长度为nnn的序列A,BA,BA,B.要求划分成若干段满足 对于任何i<ji<ji& ...

  7. 洛谷P2463 [SDOI2008]Sandy的卡片(后缀数组SA + 差分 + 二分答案)

    题目链接:https://www.luogu.org/problem/P2463 [题意] 求出N个串中都出现的相同子串的最长长度,相同子串的定义如题:所有元素加上一个数变成另一个,则这两个串相同,可 ...

  8. 2016 Multi-University Training Contest 1 GCD【RMQ+二分】

    因为那时候没怎么补所以就分到了未搞分组里!!!然后因为标题如此之屌吧= =点击量很高,然后写的是无思路,23333,估计看题人真的是觉得博主就是个撒缺.废话不多说了,补题... update2016/ ...

  9. HDU5726 GCD(rmq+二分)

    这道题是2016第一场多校的1004,趁机优化了一发 题意:10W长度的数组,10W个询问,让你回答任意两点间的gcd和全局相同gcd的数量 思路:用由于gcd一段固定以后具有单调性的,用rmq nl ...

最新文章

  1. 解决windows版 duet display无法正常连接 【看完就会】
  2. Linux安装FTP服务-----vsftpd
  3. Javascript aop(面向切面编程)之around(环绕)
  4. JavaScript 开发的40个经典技巧
  5. https证书互信解决方案—创建私有CA并申请证书
  6. 2499元起!vivo首款旗舰级平板正式发布 全系标配8GB运行内存
  7. 中国电信:1月5G套餐用户数净增826万户
  8. truffle 安装以及基本指令
  9. android preference-headers 属性,Android开发之PreferenceActivity的使用
  10. JavaScript、canvas小球加速和减速运动
  11. pool.map()爬取美文网标题内容
  12. 刘徽与《九章算术》《海岛算经》简介
  13. 马斯克细分可重复使用火箭成本 两次发射就可回本儿
  14. 服务器间文件拷贝显示busy,网站解决和优化Server is too busy的一些方法
  15. 动态规划之挖金矿问题(Python and Java)
  16. vue3 + crypto-js加密解密(普通版本/TS版本)
  17. hdu--6045 Is Derek Lying
  18. 游戏开发的人员结构(1)
  19. linux定时任务每两天,linux定时任务
  20. Flutter必备——Dart入门(上)

热门文章

  1. python核心模块之pickle和cPickle讲解
  2. 建立学生选课表 mysql 语句_MySQL常用SQL语句(Python实现学生、课程、选课表增删改查)...
  3. php中页面平滑回到顶部代码,原生JS实现平滑回到顶部组件_javascript技巧
  4. html盒模型向上浮动,HTML5盒模型、浮动和定位
  5. 工作原理是什么_磁翻板液位计工作原理是什么?
  6. ros发布节点信息python_vscode开发ROS1(13)-python实现话题通信(msg)
  7. 在栈中压入一个字符串c语言,面试题 31:栈的压入、弹出序列
  8. python导入文件列行_python读写csv文件并增加行列的实例代码
  9. c++随机数函数rand()
  10. c语言字符串中取最大字符串,使用C语言提取子字符串及判断对称子字符串最大长度...