• Luogu P2408 不同子串个数|后缀数组


    题目链接

    题意:给定一个字符串,求不同子串的个数。

    一道很好的后缀数组题。

    考虑子串一定是一个后缀的前缀,所以我们可以求出用后缀数组求出$LCP​$(最长公共前缀),再求出每个后缀对答案的贡献。

    即,长度减去出现过的前缀(一定是与排名前一个的后缀的$LCP​$,因为对于一个后缀$i​$,与其最相似的是排名与其相邻的后缀)。因此,以第$i​$个字符(从0开始数)开头的后缀的贡献值为$n-i-height_{rank_i}​$。

    上代码

    //这里字符串是从0开始的
    #include<bits/stdc++.h>
    using namespace std;
    char st[100100];int rank[100100],nrank[100100],sa[100100],p[100100],height[100100],sum[100100],n;
    long long ans;
    bool same(int x,int y,int l)
    {
        if (rank[x]!=rank[y]) return false;
        if ((x+l>=n&&y+l<n) || (x+l<n&&y+l>=n)) return false;
        if (x+l>=n&&y+l>=n) return true;
        return rank[x+l]==rank[y+l];
    }//判重
    void calc_height()
    {
    //根据性质,计算height数组(当前字符串与排名是其上一个的字符串的LCP)
        int j=0;
        for (int i=0;i<n;i++)
        {
            if (j) j--;
            if (!rank[i]) 
            {
                ans+=n-sa[rank[i]];j=0;//与前面没有LCP,即整串都可以作为答案
                continue;
            }
            for (int k=sa[rank[i]]+j,l=sa[rank[i]-1]+j;;)
            {
                if (st[k]==st[l]) j++,k++,l++;else break;
            }
            height[rank[i]]=j;
            ans+=n-sa[rank[i]]-j;//计算答案,ans要用long long,否则会挂。
        }
    }
    int main()
    {
        scanf("%d",&n);
        scanf("%s",&st);
        for (int i=0;i<n;i++)
            sum[rank[i]=int(st[i])]++;
        for (int i=1;i<=128;i++)
            sum[i]+=sum[i-1];
        for (int i=n-1;i>=0;i--)
            sa[--sum[int(st[i])]]=i;
        int maxg=max(128,n);
        for (int l=1;l<n;l<<=1)
        {
            int k=-1;
            for (int i=n-l;i<n;i++) p[++k]=i;
            for (int i=0;i<n;i++) 
              if (sa[i]-l>=0) p[++k]=sa[i]-l;
            for (int i=0;i<=maxg;i++)
              sum[i]=0;
            for (int i=0;i<n;i++)
              sum[rank[i]]++;
            for (int i=1;i<=maxg;i++) 
              sum[i]+=sum[i-1];
            for (int i=n-1;i>=0;i--)
                sa[--sum[rank[p[i]]]]=p[i];//基排求新排名
            nrank[sa[0]]=0;int ns=0;
            for (int i=1;i<n;i++)
            {
                if (same(sa[i],sa[i-1],l)) nrank[sa[i]]=ns;else nrank[sa[i]]=++ns;
            }
            for (int i=0;i<n;i++)
            {
                rank[i]=nrank[i];
                nrank[i]=0;
            }
            if (ns==n-1) break;
        }
        calc_height();
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    mysql登录等
    软工实践寒假作业(1/2)
    结对作业二——顶会热词统计的实现
    基于okhttp的安卓端网络编程
    Le vent se lève, il faut tenter de vivre
    软件评测
    一道算法题
    结对作业一
    软工实践寒假作业(2/2)
    实验六:Mininet脚本实现控制交换机行为
  • 原文地址:https://www.cnblogs.com/fmj123/p/luogu2408.html
Copyright © 2020-2023  润新知