• 后缀数组


    将字符串每个后缀按照字典序排序

    (sa:)表示排名为(i)的后缀的起始位置

    (rk:)表示起始位置为(i)的后缀的排名

    (sa[rk[i]]=i, rk[sa[i]]=i)

    通过倍增和基数排序来实现(O(n log n))的排序

    基数排序时先排第一关键字,再在第一关键字相同下排第二关键字

    第二关键字本身是有序的

    (num:)表示当前排名的个数,也就是可以存在并列(当一二关键字都相等时),当(num=n)是就排好序了

    (tp:)表示排名为(num)的后缀的位置,也是第二关键字排名为(i)的后缀的起始位置

    (rk[tp[i]])即为排名为(i)的第二关键字对应的第一关键字

    (b[rk[tp[i]]])即为当第一关键字相同时,第二关键字较大的该后缀的排名

    所以(sa[b[rk[tp[i]]]--]=tp[i])

    (rk)(tp)数组大小应开成两倍

    (LCP)最长公共前缀

    (ht:)表示(suff(sa[i]))(suff(sa[i-1]))的最长公共前缀

    (h:)表示(ht[rk[i]])(suff(i))和排序后它前一位的后缀的最长公共前缀

    (h[i] geqslant h[i-1]-1)

    所以(suff(i))和它前一位后缀的最长公共前缀至少为(h[i-1]-1)

    本质不同的子串个数:(sumlimits_{i=1}^n n-sa_i+1-ht_i=frac{n(n+1)}{2}-sumlimits_{i=1}^n ht_i)

    (code:)

    void rsort()
    {
        for(int i=0;i<=m;++i) b[i]=0;
        for(int i=1;i<=n;++i) b[rk[i]]++;
        for(int i=1;i<=m;++i) b[i]+=b[i-1];
        for(int i=n;i;--i) sa[b[rk[tp[i]]]--]=tp[i];
    }
    void SA()
    {
        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();
            memcpy(tp,rk,sizeof(rk));
            rk[sa[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 height()
    {
        int k=0;
        for(int i=1;i<=n;++i) rk[sa[i]]=i;
        for(int i=1;i<=n;++i)
        {
            if(rk[i]==1) continue;
            if(k) k--;
            int j=sa[rk[i]-1];
            while(s[i+k]==s[j+k]) k++;
            ht[rk[i]]=k;
        }
    }
    

    两个后缀的(LCP)为区间(height)数组的最小值

    (code:)

    void ST()
    {
        lg[0]=-1;
        for(int i=1;i<=n;++i) lg[i]=lg[i>>1]+1;
        for(int i=1;i<=n;++i) f[i][0]=ht[i];
        for(int j=1;j<=20;++j)
            for(int i=1;i+(1<<j)-1<=n;++i)
                f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    }
    int lcp(int l,int r)
    {
        l=rk[l],r=rk[r];
        if(l>r) swap(l,r);
        l++;
        int len=lg[r-l+1];
        return min(f[l][len],f[r-(1<<len)+1][len]);
    }
    
  • 相关阅读:
    2018面试题
    输入对象和数量制造批量假数据
    前端监控和前端埋点方案设计--摘抄
    给页面上所有的a标签增加随机数每次点击保证最新
    给所有ajax请求增加随机数
    打印2018年的日历
    为图片添加文字 canvas
    地图搜索地图定位标注
    地图拖拽定位
    智能搜索地图
  • 原文地址:https://www.cnblogs.com/lhm-/p/12229570.html
Copyright © 2020-2023  润新知