冲刺NOI2017 (24) A (后缀数组 回滚莫队)

来源:互联网 发布:sql统计表字段数 编辑:程序博客网 时间:2024/06/06 02:20

题目大意

给出一个长度为n的字符串s,提出q个询问,对于每个询问要求回答:右端点在区间[l,r]的所有前缀,最长公共后缀最长的一对前缀的最长公共后缀的长度是多少。


题解

因为要在原串的前缀上分析后缀十分的别扭,所以对这个问题略作转化:将原串翻转过来,求左端点在区间[l,r]内的后缀的lcp(最长公共前缀)。同时不要忘记把询问也翻转到同样的姿势。

问题已经转化为了:左端点在区间[l,r]的所有后缀,lcp最长的一对后缀的lcp长度。因为要求出后缀间的lcp,所以处理出后缀数组和高度数组。因为每对后缀的lcp就是两者在sa数组中的位置间height的最小值,所以对于一些后缀,他们间的最大lcp就是分布在sa数组上的若干个散点中,若干个点对的lcp的最大值。

只分析到这里的话,对于每个询问可以O((rl+1)2)地枚举后缀对,在已经预处理过height数组的st表的情况下,对于每一个后缀对可以O(1)地求出lcp并维护最大值。

但是这样的复杂度是非常不可观的,分析后缀间lcp的性质:若有三个左端点分别为a,b,c的后缀,且在sa数组上rnk[a]<rnk[b]<rnk[c],若记lcp(x,y)为左端点分别在x,y的后缀的lcp的话,那么一定满足lcp(a,b)lcp(a,c)lcp(b,c)lcp(a,c)

有了上面的性质,虽然无法直接优化上面的做法,但是将所有的询问离线后这个性质就显得很有用了。考虑将所有询问离线后莫队,对于每个添加操作,就相当于在原有的一堆散点中插入了一个散点,新插入的点两侧的点原有的答案就一定不比新点与两侧的答案优,这些过程用几个多重集进行即可。对于每个删除操作就相当于添加操作的逆操作。这样做的时间复杂度为O(n1.5logn),莫队常数挺大的,所以除非你真的很wys请不要尝试卡常通过….

考虑如何去掉这个log。发现添加操作是无法去掉log的,那么我们就干脆去掉添加操作,用只带删除的回滚莫队来解决这个问题。考虑用删除操作时用双向链表维护,每删除一个位置就修改其前驱和后继元素的指针并更新答案。这样就可以去掉维护位置的log了,因为虽然修改的次数很多,但是对修改后的答案进行的查询只有q个,所以考虑用分块维护链表中相邻位置的两后缀的lcp的长度,这样就可以每次O(1)修改,O(n)查询了。这样总时间复杂度就降到了O(n1.5)

回滚莫队见这里。


代码

#include <cstdio>#include <iostream>//#include <ctime>#include <algorithm>using namespace std;inline int read() {    register int val=0; char ch;    while(~(ch=getchar()) && (ch<'0' || ch>'9')); val=ch-'0';    while(~(ch=getchar()) && (ch>='0' && ch<='9')) val=(val<<1)+(val<<3)+ch-'0';    return val;}const int maxn=int(1e5)+111;int n,m;char s[maxn];int siz, num, bel[maxn];int ans[maxn];struct Query {    int l,r,id;    Query() {}    Query(int a,int b,int c):l(a),r(b),id(c) {}    bool operator < (const Query &b) const {        return bel[l]==bel[b.l]?r>b.r:bel[l]<bel[b.l];    }}q[maxn];void Read() {    register int i;    n=read(), m=read();    scanf("%s",s);    reverse(s,s+n);    while(siz*siz<n) ++siz;    for(i=1;i<=n;++i)        bel[i]=(i-1)/siz+1;    num=bel[n];    int l,r;    for(i=1;i<=m;++i) {        l=read(), r=read();        q[i]=Query(n-r+1,n-l+1,i);    }    sort(q+1,q+1+m);    return;}int K;int sa[maxn], rnk[maxn], tmp[maxn];int h[maxn];int st[maxn][20];inline bool cmp(const int &a,const int &b) {    if(rnk[a]!=rnk[b]) return rnk[a]<rnk[b];    else {        int x=a+K<n?rnk[a+K]:-1;        int y=b+K<n?rnk[b+K]:-1;        return x<y;    }}void Init_sa() {    register int i;    for(i=0;i<=n;++i) {        sa[i]=i;        rnk[i]=i<n?s[i]:-1;    }    for(K=1;K<=n;K<<=1) {        sort(sa,sa+1+n,cmp);        tmp[sa[0]]=1;        for(i=1;i<=n;++i)            if(cmp(sa[i-1],sa[i])) tmp[sa[i]]=tmp[sa[i-1]]+1;            else tmp[sa[i]]=tmp[sa[i-1]];        for(i=0;i<=n;++i)            rnk[i]=tmp[i];    }    return;}void Init_lcp(int *lcp) {    register int i, j, h=0;    for(i=0;i<=n;++i) {        rnk[sa[i]]=i;        lcp[i]=0;    }    lcp[sa[0]]=0;    for(i=0;i<n;++i) {        j=sa[rnk[i]-1];        if(h>0) --h;        while(i+h<n && j+h<n && s[i+h]==s[j+h]) ++h;        lcp[rnk[j]]=h;    }    return;}#define bin(k) (1<<(k))int lg2[maxn];void Init_lg2() {    register int i,j;    for(i=1,j=0;i<maxn;i<<=1,++j) lg2[i]=j;    for(i=3;i<maxn;++i) if(!lg2[i]) lg2[i]=lg2[i-1];    return;}void Init_st() {    register int i,j;    for(i=1;i<=n;++i)         st[i][0]=h[i];    int upp=lg2[n];    for(i=1;i<=upp;++i)    for(j=1;j+bin(i)-1<=n;++j)        st[j][i]=min(st[j][i-1],st[j+bin(i-1)][i-1]);    return;}inline int RMQ(const int &l,const int &r) {    int lg=lg2[r-l+1];    return min(st[l][lg],st[r-bin(lg)+1][lg]);}void Init() {    Init_lg2();    Init_sa();    Init_lcp(h);    Init_st();    return;}bool used[maxn];int pre[maxn], nex[maxn];int cnt[maxn], bcnt[400];int getmax() {    register int i,j;    for(i=num;!bcnt[i];--i);    for(j=i*siz;!cnt[j];--j);    return j;}void pre_calc(int id) {    register int i;    for(i=0;i<=n;++i) {        used[i]=false;        cnt[i]=bcnt[bel[i]]=pre[i]=nex[i]=0;    }    for(i=(id-1)*siz+1;i<=n;++i)        used[rnk[i-1]]=true;    int cur, last=0;    for(i=1;i<=n;++i) if(used[i]) {        pre[i]=last;        nex[last]=i;        if(last) ++cnt[cur=RMQ(last,i-1)], ++bcnt[bel[cur]];        last=i;    }    nex[last]=n+1;    return;}#define mp make_pairpair<int,int> s1[maxn], s2[maxn], s3[maxn]; //pre, nex, cntint t1,t2,t3;void Del(int id,int sign) {    id=rnk[id-1];    int cur, tot=0;    if(pre[id]>=1) {        cur=RMQ(pre[id],id-1); ++tot;        if(sign) s3[++t3]=mp(cur,-1);        --cnt[cur], --bcnt[bel[cur]];        if(sign) s2[++t2]=mp(pre[id],nex[pre[id]]);        nex[pre[id]]=nex[id];    }    if(nex[id]<=n) {        cur=RMQ(id,nex[id]-1); ++tot;        if(sign) s3[++t3]=mp(cur,-1);        --cnt[cur], --bcnt[bel[cur]];        if(sign) s1[++t1]=mp(nex[id],pre[nex[id]]);        pre[nex[id]]=pre[id];    }    if(tot==2) {        cur=RMQ(pre[id],nex[id]-1);        if(sign) s3[++t3]=mp(cur,1);        ++cnt[cur], ++bcnt[bel[cur]];    }    return;}void Rollback() {    while(t1) {        pre[s1[t1].first]=s1[t1].second;        --t1;    }    while(t2) {        nex[s2[t2].first]=s2[t2].second;        --t2;    }    while(t3) {        cnt[s3[t3].first]-=s3[t3].second;        bcnt[bel[s3[t3].first]]-=s3[t3].second;        --t3;    }    return;}int Mo(int pos,int id) {    register int i=pos, ql=(id-1)*siz+1, qr=n;    pre_calc(id);    for(i=pos;bel[q[i].l]==id;++i) {        while(qr>q[i].r) Del(qr--,0);        while(ql<q[i].l) Del(ql++,1);        ans[q[i].id]=getmax();        Rollback(); ql=(id-1)*siz+1;    }    return i;}void Solve() {    register int i,pos=1;    for(i=1;i<=num;++i)        pos=Mo(pos,i);    return;}int main() {//  freopen("a.in","r",stdin);//  freopen("a.out","w",stdout);//  double t1=clock();    Read();    Init();    Solve();    for(int i=1;i<=m;++i)        printf("%d\n",ans[i]);//  printf("%.3lfsec\n",(clock()-t1)/CLOCKS_PER_SEC);    return 0;}
原创粉丝点击