HDU 6194 后缀数组

题意:求出现恰好 k次的子串(可以重叠)的个数;

分析:

刚开始想到了是后缀数组,但是有什么性质,具体怎么做的没有想到。回到主题来:

连续 k 次,说明这 k 个后缀排序后在一起,每次枚举 长度的为 k 的区间,用RMQ算出最长公共前缀长度,这里就有 len 个子串是 符合满足 k 次的,但是又有可能过短而不止出现了 k次,那么有多少呢?

那么就是 a 不属于,len - height[j+1],前面也一样。也就是每次只算他独有的子串,但是你可能问 len > k 的时候,可以从新组成一组子串!

没关系,他会在下一个 k 的区间内出现~~~

到这里,离成功就只差一步了, k = 1 的时候,什么意思? 独一无二的子串个数,这时区间内只有一个后缀,那么就有 len - sa[i] 个后缀是至少有 1 次的,再次去掉 那些>1 次的子串(height[i] ,height[j+1])

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;

const int maxn = 2000000+5;

int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int sa[maxn];
int r[maxn];

int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++) ws[i]=0;
    for(i=0; i<n; i++) ws[x[i]=r[i]]++;
    for(i=1; i<m; i++) ws[i]+=ws[i-1];
    for(i=n-1; i>=0; i--) sa[--ws[x[i]]]=i;
    for(j=1,p=1; p<n; j*=2,m=p)
    {
        for(p=0,i=n-j; i<n; i++) y[p++]=i;
        for(i=0; i<n; i++) if(sa[i]>=j) y[p++]=sa[i]-j;
        for(i=0; i<n; i++) wv[i]=x[y[i]];
        for(i=0; i<m; i++) ws[i]=0;
        for(i=0; i<n; i++) ws[wv[i]]++;
        for(i=1; i<m; i++) ws[i]+=ws[i-1];
        for(i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i];
        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1; i<n; i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
    }
    return;
}
int ranks[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
    int i,j,k=0;
    for(i=1; i<=n; i++) ranks[sa[i]]=i;
    for(i=0; i<n; height[ranks[i++]]=k)
        for(k?k--:0,j=sa[ranks[i]-1]; r[i+k]==r[j+k]; k++);
    return;
}

char str[maxn];

int f[maxn][20];
void init(int len) {
    for(int i = 1; i <= len; i++) f[i][0] = height[i];
    for(int s = 1; (1<<s)<=len; s++) {
        int tmp = (1<<s);
        for(int i = 1; i+tmp-1<=len; i++) {
            f[i][s] = min(f[i][s-1],f[i+tmp/2][s-1]);
        }
    }
}

int cal(int l,int r) {
    int len = log2(r-l+1);
    int ans = min(f[l][len],f[r-(1<<len)+1][len]);
    return ans;
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--) {
        int k;
        scanf("%d%s",&k,str);

        int len = strlen(str);
        for(int i = 0; i < len; i++)
            r[i] = str[i] - ‘a‘ + 1;
        r[len] = 0;

        da(r,sa,len+1,130);
        calheight(r,sa,len);
        init(len);
        int ans = 0;

        for(int i = 1; i+k-1<=len; i++) {
            int j = i+k-1;
            int tmp = height[i];
            if(j+1<=len) tmp = max(tmp,height[j+1]);
            int x;
            if(k!=1) {
                x = cal(i+1,j);
            }
            else
                x = len - sa[i];
            ans +=max(0,x-tmp);
        }

        printf("%d\n",ans);

    }
    return 0;
}
时间: 2024-12-30 02:24:53

HDU 6194 后缀数组的相关文章

hdu 3948 后缀数组

The Number of Palindromes Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 2465    Accepted Submission(s): 841 Problem Description Now, you are given a string S. We want to know how many distin

hdu 3518 后缀数组

Boring counting Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 2906    Accepted Submission(s): 1201 Problem Description 035 now faced a tough problem,his english teacher gives him a string,whic

Hdu 1403(后缀数组)

题目链接 Longest Common Substring Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4077    Accepted Submission(s): 1544 Problem Description Given two strings, you have to tell the length of the Longe

HDU - 3948 后缀数组+Manacher

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3948 题意:给定一个字符串,求字符串本质不同的回文子串个数. 思路:主要参考该篇解题报告 先按照manacher的构造方法改造一遍串,然后跑一遍manacher.求出以i为中心的最长回文串长度p[i]. 然后跑一遍后缀数组,若已经求得后缀sa[i-1]对答案的贡献,然后现在计算后缀sa[i],本来是要加上以sa[i]为中心的回文串的个数p[sa[i]]. 我们可以维护一个tmp,也就是上图中蓝色的框

HDU - 1403 后缀数组初步

题意:求两个串的最长公共子串 两个串连接起来然后求高度数组 注意两个sa值必须分别在不同一侧 本题是用来测试模板的,回想起青岛那次翻车感觉很糟糕 #include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<string> #include<vec

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 (2017沈阳网赛-后缀数组)

转自:http://blog.csdn.net/aozil_yang/article/details/77929216 题意: 告诉你一个字符串和k , 求这个字符串中有多少不同的子串恰好出现了k 次. 思路: 后缀数组. 我们先考虑至少出现k 次的子串, 所以我们枚举排好序的后缀i (sa[i]) . k段k 段的枚举. 假设当前枚举的是 sa[i]~sa[i + k -1] 那么假设这一段的最长公共前缀  是L 的话. 那么就有L 个不同的子串至少出现了k次. 我们要减去至少出现k + 1次

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

【后缀数组】【RMQ】HDU 6194 - string string string (2017ICPC沈阳网络赛)

string string string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) 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