欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C-CSL的密码(后缀数组-求本质不同的子串的数量)

程序员文章站 2024-03-15 20:12:00
...

https://ac.nowcoder.com/acm/contest/551/C

后缀数组LCP的应用

后缀数组两篇不错的blog
https://www.cnblogs.com/victorique/p/8480093.html
http://www.cnblogs.com/zwfymqz/p/8413523.html#_label5

求本质不同的子串的数量

枚举每一个后缀,第i个后缀对答案的贡献为len−sa[i]+1−height[i]

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define INF 0x3f3f3f3f;
#define fi first
#define se second
#define MP make_pair
#define PI pair<int,int>
#define lson l,m,rt<<1,ls,rs
#define rson m+1,r,rt<<1|1,ls,rs
#define test printf("here!!!\n")
using namespace std;
const int mx=1e5+10;
int n,m,maxn;
char s[mx];//rk可能存在相同的排名,但sa一定是不同的,因为位置是不同的(虽然不同位置的排名可能是一样的,但最后rk和sa肯定是不一样的,且可以有rk[sa[i]]=i,sa[rk[i]]=i的关系)
int sa[mx],tp[mx],rk[mx],rdx[mx],height[mx];//rk是sa的排行,然后第二关键字tp是依托于第一关键字sa进行排序的,即第一关键字优先于第二关键字
/*void Debug() {
    printf("*****************\n");
    printf("下标"); for (int i = 1; i <= n; i++) printf("%d ", i);     printf("\n");
    printf("sa  "); for (int i = 1; i <= n; i++) printf("%d ", sa[i]); printf("\n");
    printf("rk "); for (int i = 1; i <= n; i++) printf("%d ", rk[i]); printf("\n");
    printf("tp  "); for (int i = 1; i <= n; i++) printf("%d ", tp[i]); printf("\n");
}*/
void get_SA()
{
    for (int i=0;i<=m;++i) rdx[i]=0;
    for (int i=1;i<=n;++i) ++rdx[rk[i]];
    for (int i=1;i<=m;++i) rdx[i]+=rdx[i-1];
    for (int i=n;i>=1;--i) sa[rdx[rk[tp[i]]]--]=tp[i];
}
void SA()
{
    m=(int)'z';//m为不同字符子串
    for (int i=1;i<=n;++i) rk[i]=(int)s[i],tp[i]=i;
    get_SA();
    for (int w=1,p=0;p<n;m=p,w<<=1)
    {
        p=0;
        for (int i=n-w+1;i<=n;++i) tp[++p]=i;
        for (int i=1;i<=n;++i)
        {
            if (sa[i]>w) tp[++p]=sa[i]-w;
        }
        get_SA();
        swap(tp,rk);
        rk[sa[1]]=p=1;
        for (int i=2;i<=n;++i)
            rk[sa[i]]=(tp[sa[i-1]]==tp[sa[i]]&&((sa[i-1]+w>n&&sa[i]+w>n)||(sa[i-1]+w<=n&&sa[i]+w<=n&&tp[sa[i-1]+w]==tp[sa[i]+w])))?p:++p;//此时的tp为之前的rk,即之前的rk排行,构造新rk
    }
    //for (int i=1;i<=n;++i) printf("%d ",sa[i]);
}
void getheight()//h[i]=height[rk[i]],height[i]=h[sa[i]],hight[rk[i]]=LCP(rk[i],rk[i]-1)
{
    int k=0;
    //for (int i=1;i<=n;++i) rk[sa[i]]=i;//上面结束时肯定是正确的rk
    for (int i=1;i<=n;++i)//按第i个位置来枚举
    {
        if (rk[i]==1) continue;
        if (k) --k;//h[i]>=h[i-1]-1
        int j=sa[rk[i]-1];
        while (i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k;
        height[rk[i]]=k;
    }
    //for (int i=1;i<=n;++i) printf("%d ",height[i]);
}
int main()
{
    scanf("%d%d",&n,&maxn);
    scanf("%s",s+1);
    SA();
    getheight();
    ll ans=0;
    for (int i=1;i<=n;++i)
    {
        ans+=(n-sa[i]+1)-min(max(maxn-1,height[i]),n-sa[i]+1);
    }
    printf("%lld\n",ans);
}