[Codechef CHSTR] Chef and String - 后缀数组

[Codechef CHSTR] Chef and String

Description

每次询问 \(S\) 的子串中,选出 \(k\) 个相同子串的方案有多少种。

Solution

本题要求不是很高,\(O(n^2)\) 统计每个出现次数子串个数即可。

我因为一个lld WA了一晚上(猛然意识到要%d读入long long的时候,之前一直没有翻车的原因是开了全局,如果是局部又不初始化就瞬间gg了,然鹅这个错误本地查不出来)

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,q,m=256,sa[200005],y[200005],u[200005],v[200005],o[200005],r[200005],h[200005],T;
int buf[200005],answer[200005];
char str[200005];
int C[5005][5005];
const int modulo = 1e+9+7;

signed main()
{
    C[0][0]=1;
    for(int i=1;i<=5000;i++)
    {
        C[i][0]=1;
        for(int j=1;j<=i;j++)
        {
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%modulo;
        }
    }
    scanf("%lld",&T);
    while(T--)
    {
        memset(sa,0,sizeof sa);
        memset(y,0,sizeof y);
        memset(u,0,sizeof u);
        memset(v,0,sizeof v);
        memset(o,0,sizeof o);
        memset(r,0,sizeof r);
        memset(h,0,sizeof h);
        memset(buf,0,sizeof buf);
        memset(answer,0,sizeof answer);
        memset(str,0,sizeof str);
        scanf("%lld%lld",&n,&q);
        scanf("%s",str+1);

        for(int i=1; i<=n; i++) u[str[i]]++;
        for(int i=1; i<=m; i++) u[i]+=u[i-1];
        for(int i=n; i>=1; i--) sa[u[str[i]]--]=i;
        r[sa[1]]=1;
        for(int i=2; i<=n; i++) r[sa[i]]=r[sa[i-1]]+(str[sa[i]]!=str[sa[i-1]]);

        for(int l=1; r[sa[n]]<n; l<<=1)
        {
            memset(u,0,sizeof u);
            memset(v,0,sizeof v);
            memcpy(o,r,sizeof r);
            for(int i=1; i<=n; i++) u[r[i]]++, v[r[i+l]]++;
            for(int i=1; i<=n; i++) u[i]+=u[i-1], v[i]+=v[i-1];
            for(int i=n; i>=1; i--) y[v[r[i+l]]--]=i;
            for(int i=n; i>=1; i--) sa[u[r[y[i]]]--]=y[i];
            r[sa[1]]=1;
            for(int i=2; i<=n; i++) r[sa[i]]=r[sa[i-1]]+((o[sa[i]]!=o[sa[i-1]])||(o[sa[i]+l]!=o[sa[i-1]+l]));
        }
        {
            int i,j,k=0;
            for(int i=1; i<=n; h[r[i++]]=k)
                for(k?k--:0,j=sa[r[i]-1]; str[i+k]==str[j+k]; k++);
        }
        buf[1]=n*(n+1ll)/2ll;
        for(int i=1; i<=n; i++) buf[1]-=h[i];
        for(int i=1; i<=n; i++)
        {
            int l=h[i],r=h[i+1];
            if(l<r)
            {
                ++l;
                for(int j=i+1; j<=n+1 && l<=r; j++)
                {
                    while(r>h[j]&&l<=r)
                    {
                        buf[j-i]++;
                        --r;
                    }
                }
            }
        }
        for(int i=2;i<=n;i++) buf[1] -= buf[i];
        for(int i=1;i<=n;i++)
        {
            for(int j=i;j<=n;j++)
            {
                (answer[i]+=(C[j][i]*buf[j])%modulo) %= modulo;
            }
        }
        //for(int i=1;i<=n;i++) cout<<buf[i]<<" ";
        //cout<<endl;1
        for(int i=1;i<=q;i++)
        {
            int tmp;
            scanf("%lld",&tmp);
            if(tmp<=n) printf("%lld\n",answer[tmp]);
            else printf("0\n");
        }
    }
}

原文地址:https://www.cnblogs.com/mollnn/p/11779668.html

时间: 2024-08-26 11:13:38

[Codechef CHSTR] Chef and String - 后缀数组的相关文章

hdu 5030 Rabbit&#39;s String(后缀数组&amp;二分)

Rabbit's String Time Limit: 40000/20000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 288    Accepted Submission(s): 108 Problem Description Long long ago, there lived a lot of rabbits in the forest. One day, the

hdu 5030 Rabbit&#39;s String(后缀数组)

题目链接:hdu 5030 Rabbit's String 题目大意:给定k和一个字符串,要求将字符串拆分成k个子串.然后将每个子串中字典序最大的子串选出来,组成一个包含k个字符串的集合,要求这个集合中字典序最大的字符串字典序最小. 解题思路:网赛的时候试图搞了一下这道题,不过水平还是有限啊,后缀数组也是初学,只会切一些水题.赛后看了一下别人的题解,把这题补上了. 首先对整个字符串做后缀数组,除了处理出sa,rank,height数组,还要处理处f数组,f[i]表示说以0~sa[i]开头共有多少

POJ 3729 Facer’s string (后缀数组)

题目大意: 串1中有多少个后缀和 串2中的某个后缀 的lcp 为 k 思路分析: 先找出 长度至少为k的对数有多少. 再找出 至少为k+1的有多少 然后相减. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <map> #include <string> #define maxn 110005 using na

POJ 2406 Power String 后缀数组

这题曾经用KMP做过,用KMP 做非常的简单,h函数自带的找循环节功能. 用后缀数组的话,首先枚举循环节长度k,然后比较LCP(suffix(k + 1), suffix(0)) 是否等于len - k, 如果相等显然k就是一个循环节. 得到LCP的话可以通过预处理出所有点和0的lcp就好了.另外倍增法构造后缀数组还有用RMQ来搞lcp nlogn是不行的,会超时,所以可以dc3走起了.. #include <cstdio> #include <cstring> #include

hdu 6194 string string string(后缀数组)

题目链接:hdu 6194 string string string 题意: 给你一个字符串,给你一个k,问你有多少个子串恰好在原串中出现k次. 题解: 后缀数组求出sa后,用height数组的信息去找答案. 每次用k长度的区间去卡height数组,求出该区间的lcp. 该区间的贡献就是ans=lcp-max(height[i],height[i+k]). 如果ans<=0,就不贡献. 比如 2 aaa 后缀数组为: 1 a 2 aa 3 aaa height为 0,1,2 现在扫到[1,2],

hdu 6194 沈阳网络赛--string string string(后缀数组)

题目链接 Problem Description Uncle Mao is a wonderful ACMER. One day he met an easy problem, but Uncle Mao was so lazy that he left the problem to you. I hope you can give him a solution.Given a string s, we define a substring that happens exactly k time

POJ3729 Facer’s string 后缀数组

Facer’s string Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 1783   Accepted: 537 Description Minifacer was very happy these days because he has learned the algorithm of KMP recently. Yet his elder brother, Hugefacer, thought that Mini

hdu-6194 string string string 后缀数组 出现恰好K次的串的数量

最少出现K次我们可以用Height数组的lcp来得出,而恰好出现K次,我们只要除去最少出现K+1次的lcp即可. #include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int maxn = 100000 + 10; int t1[maxn], t2[maxn], c[maxn]; bool cmp(int

Hackerrank--Ashton and String (后缀数组)

#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define maxn 100100 int wa[maxn],wb[maxn],wv[maxn],ws[maxn]; int r[maxn],sa[maxn]; char str[maxn]; int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&a