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;
}
原文地址:https://www.cnblogs.com/fmj123/p/luogu2408.html