Description

pps又开始dota视频直播了!一群每天被pps虐的蒟蒻决定学习pps的操作技术,他们把pps在这局放的技能记录了下来,每个技能用一个字符表示。经过研究,蒟蒻们发现字典序更大的连招威力更大。于是所有蒟蒻都想学习pps最强的连招。但是他们太弱了,不能学会整个视频里的连招,只能学会陈老师一段区间间内的连招,可是这个他们求不出,于是只好向你求助。为了蒟蒻们不再被pps虐(怎么可能),请你帮帮他们。简化题意:给你一个字符串,
每次询问你一段区间的字典序最大的子串。

Input

第一行是一个字符串S,表示pps放的技能
第二行一个正整数Q,表示询问个数
接下来Q行,每行两个正整数[l,r],表示询问区间[l,r]中的字典序最大的子串。

Output

Q行,每行一个正整数,表示该区间内字典序最大的子串的起始位置。

Sample Input

Lets_go_mod_p!
5
2 2
3 3
2 5
1 10
2 9

Sample Output

2
3
3
3
3

数据范围
1<=|S|<=100000
1<=Q<=100000
1<=l<=r<=|S|

分析:
单个字符串的子串问题,我们可以考虑后缀数组
一开始可能想的比较简单,每个询问,我们只要查找区间内rak最大的后缀即可
大概因为排名靠后的后缀大概比排名靠前的后缀优秀)

然而事实并不是这样的

      0   1   2   3   4   5
s:    b   a   a   b   b   b
rak:  3   1   2   6   5   4
ask[0,3]
最优解显然是: [0,3] b a a b
而不是rak最大的:[3,3] b   

那么每个后缀到底在什么情况上回产生贡献nei?
设i<ji<ji,当前区间为[l,r][l,r][l,r]

  • rank[i]>rank[j]rank[i]>rank[j]rank[i]>rank[j],iii一定比j" role="presentation" style="position: relative;">jjj优

  • rank[i]<rank[j]rank[i]<rank[j]rank[i]且lcp(i,j)>=r−j+1lcp(i,j)>=r−j+1lcp(i,j)>=r-j+1,iii一定比j" role="presentation" style="position: relative;">jjj优
    也就是说lcplcplcp的长度太长,所以从iii开始保留的lcp" role="presentation" style="position: relative;">lcplcplcp反而多,所以iii比j" role="presentation" style="position: relative;">jjj优

  • rank[i]<rank[j]rank[i]<rank[j]rank[i]且lcp(i,j)<r−j+1lcp(i,j)<r−j+1lcp(i,j),jjj一定比i" role="presentation" style="position: relative;">iii优

综上所述,后缀iii产生贡献的区间[i,j+lcp(i,j)−1]" role="presentation" style="position: relative;">[i,j+lcp(i,j)−1][i,j+lcp(i,j)−1][i,j+lcp(i,j)-1],其中jjj为i" role="presentation" style="position: relative;">iii后面第一个大于rank[i]rank[i]rank[i]的位置
也就是说一个最优值影响的区间是连续的

对于询问的区间我们按照左端点从大到小排序

维护一个栈,栈中的每一个元素对应了一段区间。每次扫到一个左端点后,依次弹出栈顶元素,知道当前这个左端点不会影响到当前栈顶的这个区间了。
还有种情况是可能会影响到最后那个区间的一部分,这样需要在这个区间中二分一下这个区间从哪裂开。
时间复杂度是O(nlogn)O(nlogn)的。

然后可以维护一个栈,将后缀从后向前加入栈中,并且保证栈底的rak最大

栈中的每一个元素对应了一段区间,表示当前元素在区间[l,r][l,r][l,r]中贡献最大答案

每次扫到一个左端点(设为LLL)后,依次弹出栈顶元素,直到当前这个左端点可以包含当前栈顶的这个区间
也就是说,栈中所有元素能够贡献最大答案的区间都在L" role="presentation" style="position: relative;">LLL之后
那么当前询问的答案就在栈中了

但是我们不知道询问区间的右端点落到了哪个区间,我们就二分一下,
找到右端点所在区间被xxx元素控制,当前询问的答案即为x" role="presentation" style="position: relative;">xxx

tip

一开始我的LCP都是这样写的:

int LCP(int x,int y) {int r=y-x+1;r=log(r)/log(2);return min(mn[x][r],mn[y-(1<<r)+1][r]);
}

但是我们在求[x,y][x,y][x,y]的lcplcplcp时,实际上求是min(height[x+1],height[y])min(height[x+1],height[y])min(height[x+1],height[y])
所以修改如下:

int LCP(int x,int y) {int r=log(y-x)/log(2);return min(mn[x+1][r],mn[y-(1<<r)+1][r]);
}

好久不写RMQ,一上来就写错:

for (int i=1;i<20;i++)for (int j=0;j<len;j++) {if (j+(1<<i)>len) break;mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);}
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>using namespace std;const int N=100010;
int sa[N],hei[N],rak[N],wx[N],wy[N],cc[N];
int n,m,mn[N][20],ans[N],top;
char s[N];
struct node{int l,r,num;
};
node Q[N],S[N];int cmp0(const node &A,const node &B) {return A.l>B.l;                        //按照左端点从大到小排序
}int cmp(int *y,int a,int b,int k,int len) {int ra1=y[a];int rb1=y[b];int ra2=a+k>=len? -1:y[a+k];int rb2=b+k>=len? -1:y[b+k];return ra1==rb1&&ra2==rb2;
}void make_sa(int len) {int i,j,k,m,p;int *x=wx,*y=wy;m=128;for (i=0;i<m;i++) cc[i]=0;for (i=0;i<len;i++) cc[x[i]=s[i]]++;for (i=1;i<m;i++) cc[i]+=cc[i-1];for (i=len-1;i>=0;i--) sa[--cc[x[i]]]=i;for (k=1;k<=len;k<<=1) {    //k=1p=0;for (i=len-k;i<len;i++) y[p++]=i;for (i=0;i<len;i++) if (sa[i]>=k) y[p++]=sa[i]-k;for (i=0;i<m;i++) cc[i]=0;for (i=0;i<len;i++) cc[x[y[i]]]++;for (i=1;i<m;i++) cc[i]+=cc[i-1];for (i=len-1;i>=0;i--) sa[--cc[x[y[i]]]]=y[i];swap(x,y);x[sa[0]]=0;p=1;for (int i=1;i<len;i++)x[sa[i]]=cmp(y,sa[i-1],sa[i],k,len)? p-1:p++;if (p>=len) break;m=p;}
}void make_hei(int len) {for (int i=0;i<len;i++) rak[sa[i]]=i;hei[0]=0;int k=0;for (int i=0;i<len;i++) {if (!rak[i]) continue;int j=sa[rak[i]-1];if (k) k--;while (s[i+k]==s[j+k]&&i+k<len&&j+k<len) k++;hei[rak[i]]=k;}memset(mn,0x33,sizeof(mn));for (int i=0;i<len;i++) mn[i][0]=hei[i];for (int i=1;i<20;i++)for (int j=0;j<len;j++) {if (j+(1<<i)>len) break;mn[j][i]=min(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);  //j+(1<<(i-1))}
}int LCP(int x,int y) {if (x>y) swap(x,y);int r=log(y-x)/log(2);return min(mn[x+1][r],mn[y-(1<<r)+1][r]);
}int pd(int x,int y,int z) {if (rak[x]>rak[y]) return 1;   //x更优 int len=LCP(rak[x],rak[y]);if (len<z-y+1) return 0;    //y更优 return 1;
}int main()
{scanf("%s",s);n=strlen(s);make_sa(n); make_hei(n);scanf("%d",&m);for (int i=1;i<=m;i++) {scanf("%d%d",&Q[i].l,&Q[i].r);Q[i].l--; Q[i].r--;Q[i].num=i;}sort(Q+1,Q+1+m,cmp0);top=1;S[0].l=n; S[1].num=S[1].l=S[1].r=n-1;    int j;for (j=1;Q[j].l==n-1;j++) ans[Q[j].num]=n;for (int i=n-2;i>=0&&j<=m;i--) {    //插入i bool flag=0;int now=top,l,r,mid;while (top) {int l=pd(i,S[top].num,S[top].l);   int r=pd(i,S[top].num,S[top].r);if (l&&r) top--;     //i的贡献大于栈顶区间if (!l&&!r) break;if (l&&!r) {flag=1;break;}}if (flag) {now=l=S[top].l;r=S[top].r;while (l<r) {mid=(l+r)>>1;if (pd(i,S[top].num,mid)) now=max(now,mid),l=mid+1;else r=mid;}S[top].l=now+1;top++;S[top].r=now;S[top].l=S[top].num=i;}else {S[++top].num=i;S[top].l=i;S[top].r=S[top-1].l-1;}while (Q[j].l==i&&j<=m) {l=1;r=top;now=Q[j].r;    //二分右端点所在区间while (l<r) {mid=(l+r)>>1;if (now>=S[mid].l&&now<=S[mid].r) break;if (now<S[mid].l) l=mid+1;else r=mid;}ans[Q[j].num]=S[(l+r)>>1].num+1;++j;}}for (int i=1;i<=m;i++) printf("%d\n",ans[i]);return 0;
}

bzoj4453 cys就是要拿英魂!(后缀数组+单调栈+二分)相关推荐

  1. [Ahoi2013]差异[后缀数组+单调栈]

    链接 解题思路:很明显前面∑1<=i<j<=nlen(Ti)+len(Tj)\sum_{1<=i<j<=n}len(T_i)+len(T_j)∑1<=i< ...

  2. [BZOJ 3238] [AHOI 2013] 差异 【后缀数组 + 单调栈】

    题目链接:BZOJ - 3238 题目分析 显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显. 求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组. 那么 ...

  3. BZOJ3879: SvT【后缀数组+单调栈】

    Description (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始 ...

  4. POJ - 3415 Common Substrings(后缀数组+单调栈)

    题目链接:点击查看 题目大意:给出两个字符串,再给出一个k,问两个字符串中长度大于等于k的公共子串有多少个(种类可重复) 题目分析:因为涉及到了子串问题,先用后缀数组跑出height数组来,接下来如果 ...

  5. [bzoj3238]差异(后缀数组+单调栈)

    显然我们可以先把len(Ti)+len(Tj)的值先算出来,再把LCP减去.所有len(Ti)+len(Tj)的值为n*(n-1)*(n+1)/2,这个随便在纸上画一画就可以算出来的. 接下来问题就是 ...

  6. [BZOJ3238][AHOI2013]差异 [后缀数组+单调栈]

    题目地址 - GO-> 题目大意: 给定一个长度为 nn 的字符串SS,令TiTi表示它从第ii个字符开始的后缀,求以下这个式子的值: ∑1≤i<j≤nlen(Ti)+len(Tj)−2× ...

  7. 【BZOJ3879】SvT,后缀数组+单调栈维护sum

    Time:2016.08.15 Author:xiaoyimi 转载注明出处谢谢 如果有不明白的地方可以在下面评论问我 传送门 思路: 建立后缀数组求出Height 如果说对每次询问暴力求LCP累加的 ...

  8. POJ 3415 后缀数组+单调栈

    题目大意: 给定A,B两种字符串,问他们当中的长度大于k的公共子串的个数有多少个 这道题目本身理解不难,将两个字符串合并后求出它的后缀数组 然后利用后缀数组求解答案 这里一开始看题解说要用栈的思想,觉 ...

  9. bzoj 3238: [Ahoi2013]差异(后缀数组+单调栈)

    3238: [Ahoi2013]差异 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 3443  Solved: 1562 [Submit][Stat ...

  10. 【HAOI2016/BZOJ4566】找相同字符 后缀数组+单调栈

    原题走这里 鉴于我实在不是很懂单调栈和单调队列这一系列东西,所以我决定稍微具体讲一下单调栈. 恩,本题实质上就是求两个字符串的公共子串数,其中只要出现位置不同,就算是不同的子串. 处理多个字符串的经典 ...

最新文章

  1. 高精度惯性传感器如何实现全球自动化愿景?
  2. mysql设置check
  3. DL之HNN:基于HNN(subplot)将凌乱数字矩阵图像(模拟手写数字图片)实现转为最相近的阿拉伯数字
  4. PHP:验证邮箱合法性
  5. java判断两个日期是否为同一天
  6. Java Web乱码分析及解决方案
  7. Takeown--夺取文件or文件夹所有权
  8. AWK 高端大气上档次
  9. 软件测试 | 手把手教你快速掌握 Monkey 工具,指令参数+APK集合测试
  10. rsycn定时同步/备份异地主机文件
  11. javascript天生就具备类似c#中的委托功能
  12. 超详细图文介绍,华为桌面云解决方案
  13. Jsp生成静态页面(转)
  14. crysis3 android,Crytek谈安卓版《孤岛危机3》:Tegra X1图形性能OK,瓶颈是CPU
  15. Android快速启动窗口技术
  16. vsCode无法自动换行显示
  17. 中共湖南省委党校中青班学员赴腾讯云启产业基地(长沙)开展交流考察活动
  18. 打开桌面计算机投屏到扩展屏,win10电脑桌面投屏到电视教程_Win10电脑怎么投屏到电视...
  19. 3Dunet 降假阳性模型预处理
  20. 基于YOLOv3的车辆号牌定位算法【文末送书】

热门文章

  1. COMPILATION ERROR
  2. c语言程序设计21点扑克牌,C语言程序设计-21点扑克牌游戏.pdf
  3. 一壶浊酒尽余欢、今宵别梦寒!
  4. 奇怪的消费者心理:看三个趣味经济学原理
  5. 解决h5兼容ios手机浏览器下载本地文件直接打开问题。
  6. Onlyoffice安装步骤
  7. 多模态逆天图片生成,OpenAI又一力作:DALL·E 2
  8. CET-4 week9 阅读 写译
  9. 【华为机试真题详解】统计射击比赛成绩
  10. pygame:超级玛丽