bzoj 4453 cys就是要拿英魂! —— 后缀数组+单调栈+set

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

这种问题...一般先把询问离线,排序;

区间对后缀排名的影响在于一些排名大而位置靠后的后缀可能因为区间右端点截掉了后面而排名变小;

考虑如何影响:如果 i < j

rk[i] > rk[j] ,那么字典序 i 一直大于 j

rk[i] < rk[j] ,那么如果右端点 R <= j + LCP(i,j),字典序 i > j,如果 R > j + LCP(i,j),字典序 i < j

而如果对询问区间按右端点排序,右端点就是递增的;

需要处理一下 i < j , rk[i] < rk[j] 的情况,由于每个 i 只考虑暂时大于它后面第一个 j (若还有 k,之后考虑 j 暂时大于 k),所以用单调栈找到后面第一个大于的;

有类似插入、删除这样的操作,可以用 set 维护,当 R > j + LCP 后,就把 i 删除;

令 set 里面是当前有贡献的位置,也就是单调栈里的位置和虽然被弹出但暂时大于后面某个后缀的位置;

然后在位置上开个 vector 记录到这个地方贡献就失效的后缀,到了后把它们再从 set 里删除;

因为 set 里位置的贡献也是从左到右单调递减的,那么每次取 L 右边第一个就是答案;

别写错ST表!

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<vector>
#define pb push_back
using namespace std;
int const xn=1e5+5;
int n,m,tax[xn],tp[xn],sa[xn],rk[xn],sta[xn],top,dc[xn],ht[xn][20],bin[20],r[xn],ans[xn];
char s[xn];
vector<int>v[xn],com[xn];
set<int>st;
set<int>::iterator it;
struct N{int l,r,id;}q[xn];
bool cmp(N x,N y){return x.r<y.r;}
int rd()
{
  int ret=0,f=1; char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return f?ret:-ret;
}
void Rsort()
{
  for(int i=1;i<=m;i++)tax[i]=0;
  for(int i=1;i<=n;i++)tax[rk[tp[i]]]++;
  for(int i=1;i<=m;i++)tax[i]+=tax[i-1];
  for(int i=n;i;i--)sa[tax[rk[tp[i]]]--]=tp[i];
}
void work()
{
  for(int i=1;i<=n;i++)rk[i]=s[i],tp[i]=i;
  Rsort();
  for(int k=1;k<=n;k<<=1)
    {
      int num=0;
      for(int i=n-k+1;i<=n;i++)tp[++num]=i;
      for(int i=1;i<=n;i++)
    if(sa[i]>k)tp[++num]=sa[i]-k;
      Rsort(); swap(rk,tp);
      rk[sa[1]]=1; num=1;
      for(int i=2;i<=n;i++)
    rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?num:++num;
      if(num==n)break;
      m=num;
    }
}
void get()
{
  int k=0;
  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[i+k]==s[j+k])k++;
      ht[rk[i]][0]=k;
    }
  bin[0]=1; for(int i=1;i<20;i++)bin[i]=(bin[i-1]<<1);
  r[1]=0; for(int i=2;i<=n;i++)r[i]=r[i>>1]+1;
  for(int j=1;j<20;j++)
    for(int i=1;i<=n&&i+bin[j]-1<=n;i++)
      ht[i][j]=min(ht[i][j-1],ht[i+bin[j-1]][j-1]);//
}
int getlcp(int x,int y)
{
  if(x==y)return n;
  x=rk[x]; y=rk[y];
  if(x>y)swap(x,y); x++;
  int w=r[y-x+1];
  return min(ht[x][w],ht[y-bin[w]+1][w]);//
}
int main()
{
  scanf("%s",s+1); n=strlen(s+1);
  for(int i=1;i<=n;i++)dc[i]=(int)s[i];
  sort(dc+1,dc+n+1); m=unique(dc+1,dc+n+1)-dc-1;
  for(int i=1;i<=n;i++)s[i]=lower_bound(dc+1,dc+m+1,(int)s[i])-dc;
  work(); get();
  int Q=rd();
  for(int i=1;i<=Q;i++)q[i].l=rd(),q[i].r=rd(),q[i].id=i;
  sort(q+1,q+Q+1,cmp); int p=0;
  for(int i=1;i<=Q;i++)
    {
      while(p<q[i].r)
    {
      p++; int y;
      while(rk[y=sta[top]]<rk[p]&&top)//<
        {
          int pos=min(n+1,p+getlcp(y,p));
          v[pos].pb(y); com[p].pb(y);
          top--;
        }
      sta[++top]=p; st.insert(p);
      for(int i=0,x;i<v[p].size();i++)
        if(st.count(x=v[p][i]))
          {
        for(int j=0;j<com[x].size();j++)
          if(st.count(com[x][j]))st.erase(com[x][j]);
        st.erase(x);
          }
    }
      it=st.lower_bound(q[i].l);
      ans[q[i].id]=*it;
    }
  for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
  return 0;
}

 

转载于:https://www.cnblogs.com/Zinn/p/10083295.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值