zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
  • 原文地址:https://www.cnblogs.com/fmj123/p/luogu2408.html
Copyright © 2011-2022 走看看