[BZOJ]4453: cys就是要拿英魂! 单调栈+二分+hash

14 篇文章 0 订阅
4 篇文章 0 订阅

Description

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

Solution

前几天WC的讲义上有讲这个题目……
离线,把询问按照 r r r从小到大排序。对于同一个 r r r,随着 l l l的右移,答案显然也是单调右移的,我们可以维护所有可能成为答案的位置,然后询问 l o w e r _ b o u n d lower\_bound lower_bound即可。考虑怎么维护这个东西,对于两个后缀 s [ i . . n ] 、 s [ j . . n ] ( i &lt; j ) s[i..n]、s[j..n](i&lt;j) s[i..n]s[j..n](i<j),若他们的 L C P LCP LCP长度为 l e n len len,且 s [ j + l e n ] &gt; s [ i + l e n ] s[j+len]&gt;s[i+len] s[j+len]>s[i+len],那么在 r &lt; j + l e n r&lt;j+len r<j+len的时候,是 s [ i . . n ] s[i..n] s[i..n]更优,而 r ≥ j + l e n r\ge j+len rj+len时,则是 s [ j . . n ] s[j..n] s[j..n]更优,因为我们是从左到右扫 r r r,所以我们要在扫到 j + l e n j+len j+len的时候删掉 s [ i . . n ] s[i..n] s[i..n]这个后缀,所以用一个set维护这个单调栈。注意一个后缀被删后,比他更差的后缀也要同时被删除。

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=100010;
const int inf=2147483647;
const unsigned int base=233;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,m,sta[Maxn],top=0,ans[Maxn];bool mark[Maxn];
char s[Maxn];
unsigned int h[Maxn],Pow[Maxn];
unsigned int g(int l,int r){return h[l]-h[r+1]*Pow[r-l+1];}
int lcp(int a,int b)
{
    int l=1,r=n-max(a,b)+1;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(g(a,a+mid-1)==g(b,b+mid-1))l=mid+1;
        else r=mid-1;
    }
    return l-1;
}
set<int>S;
vector<int>p[Maxn],del[Maxn];
vector<pa>query[Maxn];
bool dfs(int x)
{
    mark[x]=true;S.erase(x);
    for(int i=0;i<p[x].size();i++)
    if(!mark[p[x][i]])dfs(p[x][i]);
}
int main()
{
    scanf("%s",s+1);n=strlen(s+1);
    h[n+1]=0;for(int i=n;i;i--)h[i]=h[i+1]*base+s[i];
    Pow[0]=1;for(int i=1;i<=n;i++)Pow[i]=Pow[i-1]*base;
    m=read();
    for(int i=1;i<=m;i++)
    {
        int l=read(),r=read();
        query[r].push_back(make_pair(l,i));
    }
    for(int r=1;r<=n;r++)
    {
        S.insert(r);
        while(top)
        {
            int t=lcp(r,sta[top]);
            if(s[r+t]<s[sta[top]+t])break;
            p[r].push_back(sta[top]);del[r+t].push_back(sta[top]);top--;
        }
        sta[++top]=r;
        for(int i=0;i<del[r].size();i++)
        if(!mark[del[r][i]])dfs(del[r][i]);
        for(int i=0;i<query[r].size();i++)
        {
            pa t=query[r][i];
            ans[t.second]=*S.lower_bound(t.first);
        }
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值