后缀数组(无讲解)

BZO2754: [SCOI2012]喵星球上的点名

题目链接

分析:

  • 把姓和名中间用一个分隔符分开,和询问串一起建立后缀数组。
  • 后缀数组上每个位置存对应串的标号。对于一个询问串(T),找到他对应的位置。
  • 考虑和他的lcp>=len(T)的位置都是合法的。左右二分/倍增提取出这样的区间。
  • 那么第一问转化成了区间颜色数量。树状数组即可。
  • 第二问相当于有若干次区间 只在第一次出现的位置 加。然后查询每个位置的和。方法类似,不过这次我们扫一遍序列。
  • 假设当前位置为i。先加入左端点是i的询问,在i处+1。然后处理这个点的信息。ans[id[i]] += [pre,i]的区间和(pre是上一次和i相等的位置)然后处理右端点等于i的询问,在该询问的左端点处-1。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define rep(n) for(i=1;i<=n;i++)
#define per(n) for(i=n;i;i--)
#define N 500050
int w[N],sa[N],ht[N],rk[N],f[20][N],n,m,ln;
int bg[N],ed[N],ws[N],wa[N],wb[N],Lg[N],id[N];
int ql[N],qr[N],ans1[N],now[N],nxt[N],c[N],pid[N];
int ans2[N],ke[N];
void fix(int x,int v) {
    for(;x<=ln;x+=x&(-x)) c[x]+=v;
}
int inq(int x) {
    int re=0;
    for(;x;x-=x&(-x)) re+=c[x]; return re;
}
struct A {
    int l,r,id;
    bool operator < (const A &x) const {
        return l == x.l ? r < x.r : l < x.l;
    }
}q[N];
priority_queue<pair<int,int> >pq;
void build_sa(int n,int m) {
    int i,j,p,*x=wa,*y=wb;
    rep(m) ws[i]=0;
    rep(n) ws[x[i]=w[i]]++;
    rep(m) ws[i]+=ws[i-1];
    per(n) sa[ws[x[i]]--]=i;
    for(j=1;j<n;j<<=1) {
        p=0;
        for(i=n;i>n-j;i--) y[++p]=i;
        rep(n) if(sa[i]>j) y[++p]=sa[i]-j;
        rep(m) ws[i]=0;
        rep(n) ws[x[i]]++;
        rep(m) ws[i]+=ws[i-1];
        per(n) sa[ws[x[y[i]]]--]=y[i];
        swap(x,y);
        m=1;
        rep(n) {
            x[sa[i]]=(y[sa[i]]==y[sa[i+1]]&&y[sa[i]+j]==y[sa[i+1]+j])?m:m++;
        }
        if(m>n) break;
    }
    rep(n) rk[sa[i]]=i;
    p=0;
    rep(n) if(rk[i]!=n) {
        j=rk[i]+1;
        for(;w[i+p]==w[sa[j]+p];p++) ;
        ht[j]=p;
        if(p) p--;
    }
    Lg[0]=-1;
    rep(n) Lg[i]=Lg[i>>1]+1,f[0][i]=ht[i];
    for(i=1;(1<<i)<=n;i++) {
        for(j=1;j<=n-(1<<i)+1;j++) {
            f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
        }
    }
}
int gm(int l,int r) {
    int len=Lg[r-l+1];return min(f[len][l],f[len][r-(1<<len)+1]);
}
int lcp(int x,int y) {
    x=rk[x], y=rk[y];
    if(x>y) swap(x,y);
    if(x==y) return ln-sa[x]+1;
    return gm(x+1,y);
}
void prt(int *a) {
    int i;
    for(i=1;i<=ln;i++) printf("%d ",a[i]); puts("");
}
int main() {
    scanf("%d%d",&n,&m);
    int i,x,y,j;
    int tmp=10000;
    for(i=1;i<=n;i++) {
        scanf("%d",&x);
        bg[i]=ln+1;
        while(x--) {
            scanf("%d",&y); y++;
            w[++ln]=y;
        }
        w[++ln]=++tmp;

        scanf("%d",&x);
        while(x--) {
            scanf("%d",&y); y++;
            w[++ln]=y;
        }
        ed[i]=ln;
        w[++ln]=++tmp;
    }
    for(i=1;i<=m;i++) {
        scanf("%d",&x);
        bg[i+n]=ln+1;
        while(x--) {
            scanf("%d",&y); y++;
            w[++ln]=y;
        }
        ed[i+n]=ln;
        w[++ln]=++tmp;
    }
    build_sa(ln,tmp);
    for(i=1;i<=n;i++) for(j=bg[i];j<=ed[i];j++) id[j]=i;
    for(i=1;i<=ln;i++) pid[i]=id[sa[i]];
    // for(i=1;i<=m;i++) for(j=bg[i+n];j<=ed[i+n];j++) id[j]=i+n;
    //prt(id);
    //prt(w);
    for(i=1;i<=m;i++) {
        x=rk[bg[i+n]];
        int len=ed[i+n]-bg[i+n]+1;
        //printf("x=%d len=%d\n",x,len);
        int l=1,r=x;
        while(l<r) {
            int mid=(l+r)>>1;
            if(x==mid||gm(mid+1,x)>=len) r=mid;
            else l=mid+1;
        }
        ql[i]=l;
        l=x,r=ln+1;
        while(l<r) {
            int mid=(l+r)>>1;
            if(x==mid||gm(x+1,mid)>=len) l=mid+1;
            else r=mid;
        }
        qr[i]=l-1;
        q[i]=(A){ql[i],qr[i],i};

        /*printf("Q%d\n",i);
        printf("ql=%d, qr=%d\n",ql[i],qr[i]);
        for(j=ql[i];j<=qr[i];j++) {
            printf("%d\n",id[sa[j]]);
        }
        puts("");*/
    }
    sort(q+1,q+m+1);
    for(i=ln;i;i--) {
        nxt[i]=now[pid[i]];
        now[pid[i]]=i;
    }
    memset(now,0,sizeof(now));
    for(i=1;i<=ln;i++) {
        if(pid[i]&&!now[pid[i]]) {
            now[pid[i]]=1;
            fix(i,1);
        }
    }
    j=1;
    for(i=1;i<=m;i++) {
        for(;j<q[i].l;j++) {
            if(pid[j]) {
                fix(j,-1);
                if(nxt[j]) fix(nxt[j],1);
            }
        }
        ans1[q[i].id]=inq(q[i].r);
    }
    for(i=1;i<=m;i++) printf("%d\n",ans1[i]);
    memset(now,0,sizeof(now));
    memset(c,0,sizeof(c));
    j=1;
    for(i=1;i<=ln;i++) {
        for(;q[j].l==i;j++) fix(i,1),pq.push(make_pair(-q[j].r,q[j].l));
        if(pid[i]) {
            ans2[pid[i]]+=inq(i)-inq(now[pid[i]]);
            now[pid[i]]=i;
        }
        while(!pq.empty()&&pq.top().first==-i) {
            fix(pq.top().second,-1); pq.pop();
        }
    }
    for(i=1;i<=n;i++) printf("%d ",ans2[i]);
}
/*
2 3
6 8 25 0 24 14 8 6 18 0 10 20 24 0
7 14 17 8 7 0 17 0 5 8 25 0 24 0
4 8 25 0 24
4 7 0 17 0
4 17 0 8 25
*/

BZOJ4556: [Tjoi2016&Heoi2016]字符串

题目链接

  • 二分答案mid。然后像上一道题那样,根据[c,d]找一个合法区间ht>=mid。是否存在一个位置的a<=sa[i]<=b-mid+1。主席树上查即可。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <map>
#include <queue>
#include <stack>
#include <cctype>
#include <vector>
#include <set>
#include <string>
using namespace std;
#define N 100050
#define rep(n) for(i=1;i<=n;i++)
#define per(n) for(i=n;i;i--)
char w[N];
int rr[N],rk[N],sa[N],ht[N],f[20][N],n,m;
int ws[N],wa[N],wb[N],Lg[N],root[N];
int siz[N*30],ls[N*30],rs[N*30],cnt;
void update(int l,int r,int x,int &p,int q) {
    p=++cnt; ls[p]=ls[q]; rs[p]=rs[q]; siz[p]=siz[q]+1;
    if(l==r) return ;
    int mid=(l+r)>>1;
    if(x<=mid) update(l,mid,x,ls[p],ls[q]);
    else update(mid+1,r,x,rs[p],rs[q]);
}
void build_sa(int n,int m) {
    int i,j,p,*x=wa,*y=wb;
    rep(m)ws[i]=0;
    rep(n)ws[x[i]=rr[i]]++;
    rep(m)ws[i]+=ws[i-1];
    per(n)sa[ws[x[i]]--]=i;
    for(j=1;j<n;j<<=1) {
        p=0;
        for(i=n;i>n-j;i--) y[++p]=i;
        rep(n) if(sa[i]>j) y[++p]=sa[i]-j;
        rep(m) ws[i]=0;
        rep(n) ws[x[i]]++;
        rep(m) ws[i]+=ws[i-1];
        per(n) sa[ws[x[y[i]]]--]=y[i];
        swap(x,y); m=1;
        rep(n) {
            x[sa[i]]=(y[sa[i]]==y[sa[i+1]]&&y[sa[i]+j]==y[sa[i+1]+j])?m:m++;
        }
        if(m>n) break;
    }
    rep(n) rk[sa[i]]=i;
    p=0;
    rep(n) if(rk[i]!=n) {
        j=rk[i]+1;
        for(;w[i+p]==w[sa[j]+p];p++) ;
        ht[j]=p;
        if(p) p--;
    }
    Lg[0]=-1;
    rep(n) Lg[i]=Lg[i>>1]+1,f[0][i]=ht[i];
    for(i=1;(1<<i)<=n;i++) {
        for(j=1;j+(1<<i)-1<=n;j++) {
            f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
        }
    }
}
int gm(int l,int r) {
    int len=Lg[r-l+1];
    return min(f[len][l],f[len][r-(1<<len)+1]);
}
int l1,l2,r1,r2,OK;
void query(int l,int r,int x,int y,int p,int q) {
    if(OK) return ;
    if(x<=l&&y>=r) {
        if(siz[q]-siz[p]>0) OK=1;
        return ;
    }
    int mid=(l+r)>>1;
    if(x<=mid) query(l,mid,x,y,ls[p],ls[q]);
    if(y>mid) query(mid+1,r,x,y,rs[p],rs[q]);
}
bool check(int mid) {
    int x=rk[l2];
    int d=0,p=x,ql,qr;
    for(d=1;x-d>=1&&gm(x-d+1,x)>=mid;d<<=1) ;
    for(d>>=1;d;d>>=1) {
        if(p-d>=1&&gm(p-d+1,x)>=mid) p-=d;
    }
    ql=p;

    d=0,p=x;
    for(d=1;x+d<=n&&gm(x+1,x+d)>=mid;d<<=1) ;
    for(d>>=1;d;d>>=1) {
        if(p+d<=n&&gm(x+1,p+d)>=mid) p+=d;
    }
    qr=p;

    OK=0;
    query(1,n,l1,r1-mid+1,root[ql-1],root[qr]);
    return OK;
}
int main() {
    scanf("%d%d%s",&n,&m,w+1);
    int i;
    for(i=1;i<=n;i++) rr[i]=w[i]-'a'+1;
    build_sa(n,255);
    rep(n) {
        update(1,n,sa[i],root[i],root[i-1]);
    }
    while(m--) {
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        int l=0,r=min(r1-l1,r2-l2)+2;
        while(l<r) {
            int mid=(l+r)>>1;
            if(check(mid)) l=mid+1;
            else r=mid;
        }
        printf("%d\n",l-1);
    }
}

BZOJ4199: [Noi2015]品酒大会

题目链接

分析:

  • 把sa[]按ht排序。并查集维护即可。
  • 维护连通块内方案数和最大值。由于有负数需要再维护一个最小值。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define N 300050
typedef long long ll;
#define rep(n) for(i=1;i<=n;i++)
#define per(n) for(i=n;i;i--)
int rr[N],n,val[N],sa[N],rk[N],ht[N],ws[N],wa[N],wb[N];
int t[N],siz[N],fa[N];
ll ans1[N],ans2[N];
int mn[N],mx[N];
char w[N];
int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
void build_sa(int n,int m) {
    int i,j,p,*x=wa,*y=wb;
    rep(m) ws[i]=0;
    rep(n) ws[x[i]=rr[i]]++;
    rep(m) ws[i]+=ws[i-1];
    per(n) sa[ws[x[i]]--]=i;
    for(j=1;j<n;j<<=1) {
        p=0;
        for(i=n;i>n-j;i--) y[++p]=i;
        rep(n) if(sa[i]>j) y[++p]=sa[i]-j;
        rep(m) ws[i]=0;
        rep(n) ws[x[i]]++;
        rep(m) ws[i]+=ws[i-1];
        per(n) sa[ws[x[y[i]]]--]=y[i];
        swap(x,y);
        m=1;
        rep(n) {
            x[sa[i]]=(y[sa[i]]==y[sa[i+1]]&&y[sa[i]+j]==y[sa[i+1]+j])?m:m++;
        }
        if(m>n) break;
    }
    rep(n) rk[sa[i]]=i;
    p=0;
    rep(n) if(rk[i]!=n) {
        j=rk[i]+1;
        for(;w[i+p]==w[sa[j]+p];p++) ;
        ht[j]=p;
        if(p) p--;
    }
}
inline bool cmp(const int &x,const int &y) {return ht[x]>ht[y];}
int main() {
    scanf("%d%s",&n,w+1);
    int i;
    rep(n) ans2[i]=-1ll<<60;
    for(i=1;i<=n;i++) scanf("%d",&val[i]);
    for(i=1;i<=n;i++) rr[i]=w[i]-'a'+1;
    build_sa(n,255);
    for(i=1;i<=n;i++) t[i]=i,fa[i]=i,mn[i]=mx[i]=val[sa[i]],siz[i]=1;
    sort(t+1,t+n+1,cmp);
    for(i=1;i<=n;i++) {
        int x=t[i],y=x-1;
        if(x==1) continue;
        int dx=find(x),dy=find(y);
        ans1[ht[x]]+=ll(siz[dx])*siz[dy];
        ans2[ht[x]]=max(ans2[ht[x]],max(ll(mn[dx])*mn[dy],ll(mx[dx])*mx[dy]));
        fa[dx]=dy; siz[dy]+=siz[dx]; mn[dy]=min(mn[dy],mn[dx]); mx[dy]=max(mx[dy],mx[dx]);
    }
    per(n) ans1[i-1]+=ans1[i],ans2[i-1]=max(ans2[i-1],ans2[i]);
    rep(n) printf("%lld %lld\n",ans1[i-1],ans1[i-1]?ans2[i-1]:0);
}

BZOJ3230: 相似子串

分析:

  • 考虑sa数组每一位都对应一些本质不同字符串。这个肯定是按字典序来的。
  • 维护n-sa[i]+1-ht[i]的前缀和。每次询问二分即可。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
using namespace std;
#define N 100050
typedef long long ll;
#define rep(n) for(i=1;i<=n;i++)
#define per(n) for(i=n;i;i--)
#define pi pair<int,int>
int n,m;
ll v[N],sv[N];
char w[N];
struct SA {
    int rr[N],sa[N],ht[N],ws[N],wa[N],wb[N],rk[N],Lg[N],f[20][N];
    void build_sa(int n,int m) {
        int i,j,p,*x=wa,*y=wb;
        rep(m) ws[i]=0;
        rep(n) ws[x[i]=rr[i]]++;
        rep(m) ws[i]+=ws[i-1];
        per(n) sa[ws[x[i]]--]=i;
        for(j=1;j<n;j<<=1) {
            p=0;
            for(i=n;i>n-j;i--) y[++p]=i;
            rep(n) if(sa[i]>j) y[++p]=sa[i]-j;
            rep(m) ws[i]=0;
            rep(n) ws[x[i]]++;
            rep(m) ws[i]+=ws[i-1];
            per(n) sa[ws[x[y[i]]]--]=y[i];
            swap(x,y);
            m=1;
            rep(n) x[sa[i]]=(y[sa[i]]==y[sa[i+1]]&&y[sa[i]+j]==y[sa[i+1]+j])?m:m++;
            if(m>n) break;
        }
        rep(n) rk[sa[i]]=i;
        p=0;
        rep(n) if(rk[i]!=n) {
            j=rk[i]+1;
            for(;rr[i+p]==rr[sa[j]+p];p++) ;
            ht[j]=p;
            if(p) p--;
        }
        Lg[0]=-1;
        rep(n) Lg[i]=Lg[i>>1]+1;
        rep(n) f[0][i]=ht[i];
        for(i=1;(1<<i)<=n;i++) for(j=1;j+(1<<i)-1<=n;j++) f[i][j]=min(f[i-1][j],f[i-1][j+(1<<(i-1))]);
    }
    int gm(int l,int r) {
        int len=Lg[r-l+1];
        return min(f[len][l],f[len][r-(1<<len)+1]);
    }
    pi getk(ll K) {
        int l=1,r=n+1;
        while(l<r) {
            int mid=(l+r)>>1;
            if(sv[mid]>=K) r=mid;
            else l=mid+1;
        }
        return make_pair(sa[l],int(n-sv[l]+K));
    }
    int LCP(int x,int y) {
        if(x==y) return n-x+1;
        x=rk[x],y=rk[y];
        if(x>y) swap(x,y);
        return gm(x+1,y);
    }
    int lcp(int l1,int r1,int l2,int r2) {
        int t=LCP(l1,l2);
        //printf("%d %d %d %d\n",l1,r1,l2,r2);
        //printf("t=%d\n",t);
        return min(t,min(r1-l1+1,r2-l2+1));
    }
}s1,s2;
ll SQ(ll x) {return x*x;}
int main() {
    scanf("%d%d%s",&n,&m,w+1);
    int i;
    rep(n) s1.rr[i]=w[i]-'a'+1;
    rep(n) s2.rr[i]=s1.rr[n-i+1];
    s1.build_sa(n,255);
    s2.build_sa(n,255);
    rep(n) {
        v[i]=n-s1.sa[i]+1-s1.ht[i];
        sv[i]=sv[i-1]+v[i];
    }
    while(m--) {
        ll x,y;
        scanf("%lld%lld",&x,&y);
        if(x>sv[n]||y>sv[n]) {
            puts("-1"); continue;
        }
        pi tx=s1.getk(x);
        pi ty=s1.getk(y);
        int l1=tx.first,r1=tx.second;
        int l2=ty.first,r2=ty.second;
        //printf("%d %d %d %d\n",l1,r1,l2,r2);
        printf("%lld\n",SQ(s1.lcp(l1,r1,l2,r2))+SQ(s2.lcp(n-r1+1,n-l1+1,n-r2+1,n-l2+1)));
    }
}

原文地址:https://www.cnblogs.com/suika/p/10014920.html

时间: 2024-10-12 01:28:35

后缀数组(无讲解)的相关文章

【BZOJ4278】[ONTAK2015]Tasowanie 后缀数组

[BZOJ4278][ONTAK2015]Tasowanie Description 给定两个数字串A和B,通过将A和B进行二路归并得到一个新的数字串T,请找到字典序最小的T. Input 第一行包含一个正整数n(1<=n<=200000),表示A串的长度. 第二行包含n个正整数,其中第i个数表示A[i](1<=A[i]<=1000). 第三行包含一个正整数m(1<=m<=200000),表示B串的长度. 第四行包含m个正整数,其中第i个数表示B[i](1<=B[

后缀数组(一堆干货)

其实就是将两篇论文里的东西整合在了一起,并且提供了一个比较好理解的板. 后缀数组 字符串:一个字符串S是将n个字符顺次排列形成的数组,n称为S的长度,表示为len(S).S的第i个字符表示为S[i]. 子串:字符串S的子串S[i…j],i<=j,表示从S串中从i到j这一段,也就是顺次排列S[i],S[i+1],……,S[j]形成的字符串. 后缀:后缀是指从某个位置i开始到整个字符串末尾结束的一个特殊子串.字符串S的从i开关的后缀表示为Suffix(S,i),也就是Suffix(S,i)=S[i…

[bzoj1717][Usaco2006 Dec]Milk Patterns 产奶的模式 (hash构造后缀数组,二分答案)

以后似乎终于不用去学后缀数组的倍增搞法||DC3等blablaSXBK的方法了= = 定义(来自关于后缀数组的那篇国家集训队论文..) 后缀数组:后缀数组SA是一个一维数组,它保存1..n的某个排列SA[1],SA[2],……,SA[n],并且保证Suffix(SA[i])<Suffix(SA[i+1]),1≤i<n. 也就是将S的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入SA中. height数组:定义height[i]=suffix(sa[i-1])和suffix(sa[

BZOJ 2754([SCOI2012]喵星球上的点名-后缀数组统计序列集合中子序列出现次数)

2754: [SCOI2012]喵星球上的点名 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 805  Solved: 380 [Submit][Status][Discuss] Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点名,每次读出一个串的时候,如果这个串是一个喵星人的姓或名的子串,

HDU 3518 Boring counting(后缀数组啊 求字符串中不重叠的重复出现至少两次的子串的个数)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3518 Problem Description 035 now faced a tough problem,his english teacher gives him a string,which consists with n lower case letter,he must figure out how many substrings appear at least twice,moreover

BF到KMP,再到后缀数组的字符串匹配

总结了下,关于BF算法,以及KMP.遇到后缀数组的时候突然发现,KMP还是有用的啊~~~后缀数组更简单,就是代码比较长~~ /* 记住一个公式:KMP算法=BF算法+next数组 ->1.BF算法中,只需要将j=next[j],避免回溯就行了.所以计算 next数组 ->2.计算next数组 做两个变量j,k j=0(用于记录计算P串的位置,j=0~(n-1),最有一个不用计算) k=-1(记录k在P串位置的前后串最长公共前后缀) ******理解下:0~k~(n-1)分k左右一个串,左右串中

后缀数组 &amp; 题目

后缀数组被称为字符串处理神器,要解决字符串问题,一定要掌握它.(我这里的下标全部都是从1开始) 首先后缀数组要处理出两个数组,一个是sa[],sa[i]表示排名第i为的后缀的起始位置是什么,rank[i]表示第i个字符为起始点的后缀,它的排名是什么.可以知道sa[rank[i]] = i; rank[sa[i]] = i; 由于每个后缀各不相同,至起码长度不同,所以每个后缀是不可能相等的. 解除一个值,就能在O(n)时间内得到另外一个. 定义:suffix(i)表示从[i, lenstr]这个后

彻底弄懂后缀数组

  什么叫后缀数组  首先要知道什么叫后缀 ? 比如 字符串 abcdef  那么 abcdef    bcdef    cdef     def       ef       f 就叫做后缀  也就是从最后一个字母之前的一个字母开始一直到最后一个字母(所以所 bcd不是后缀 因为没有到最后一位f)  所构成的字符串就叫做后缀 至于后缀数组能干什么?我在这就不介绍了  这不是本文的重点!本文主要讲解后缀数组应该怎么写代码! 写本文的原因 但是自己之前读过很多后缀数组的文章  短短二三十代码  却

后缀数组(1)

感觉跟字符串有关的算法都难飞了:) 首先入坑是noi day2t2,舔题解开启后缀数组副本 首先自然是模板题刷水,因为(爱情怎莫会有沧桑)懒,所以没写过基数排序,所以舔模板也舔地十分困难,最后还是跪求zl老爷讲解,,,然而当时也是美得朦胧... 不过还好之前舔了集训队论文->算法合集之<后缀数组——处理字符串的有力工具> 1 #include<stdio.h> 2 #include<string.h> 3 #define maxn 100005 4 char st