HDU 3518 Boring counting(后缀数组啊 求字符串中不重叠的重复出现至少两次的子串的个数)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3518

Problem Description

035 now faced a tough problem,his english teacher gives him a string,which consists with n lower case letter,he must figure out how many substrings appear at least twice,moreover,such apearances can not overlap each other.

Take aaaa as an example.”a” apears four times,”aa” apears two times without overlaping.however,aaa can’t apear more than one time without overlaping.since we can get “aaa” from [0-2](The position of string begins with 0) and [1-3]. But the interval [0-2] and
[1-3] overlaps each other.So “aaa” can not take into account.Therefore,the answer is 2(“a”,and “aa”).

Input

The input data consist with several test cases.The input ends with a line “#”.each test case contain a string consists with lower letter,the length n won’t exceed 1000(n <= 1000).

Output

For each test case output an integer ans,which represent the answer for the test case.you’d better use int64 to avoid unnecessary trouble.

Sample Input

aaaa
ababcabb
aaaaaa
#

Sample Output

2
3
3

Source

2010 ACM-ICPC Multi-University
Training Contest(9)——Host by HNU

题意:

如题,求字符串中不重叠的重复出现至少两次的子串的个数!

PS:

运用后缀数组的性质,

先将后缀数组的sa、rank、height数组求出来!

然后在枚举重复子串的长度i,然后再在连续(至于为什么是连续的将在下面解释)且值大于所枚举的长度i的height数组中找出最大值和最小值,

如果最大值和最小值的差大于等于所枚举的长度i,那么答案增加一;

为什么需要连续的height数组呢?

因为这样可以不用再考虑重复计数的问题,如果有相同的前缀那么他们的rank排名必定是相邻的,那么height就是连续的,前缀不同的则再做另一次判断!

代码如下:

#include <cstdio>
#include <cstring>
const int N = 1017;
int wa[N], wb[N], wv[N], ws[N];
int rank[N]; //名次数组
int  height[N]; //排名相邻的两个后缀的最长公共前缀
char str[N];
int s[N], sa[N]; //sa为后缀数组,n个后缀从小到大进行排序之后把排好序的后缀的开头位置

int Max(int a, int b)
{
    return a > b ? a:b;
}
int Min(int a, int b)
{
    return a < b ? a:b;
}
int cmp(int *r, int a, int b, int l)
{
    return r[a]==r[b] && r[a+l]==r[b+l];
}
//get_sa函数的参数n代表字符串中字符的个数,这里的n里面是包括人为在字符串末尾添加的那个0的
//get_sa函数的参数m代表字符串中字符的取值范围,是基数排序的一个参数,
//如果原序列都是字母可以直接取128,
//如果原序列本身都是整数的话,则m可以取比最大的整数大1的值。
void get_sa(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; //对长度为1的字符串排序
    for(p=1,j=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++; //更新rank数组
    }
    return;
}
void get_height(int *r, int *sa, int n) //求height数组
{
    int i, j, k=0;
    for(i=1; i<=n; i++) rank[sa[i]]=i;
    for(i=0; i<n; height[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);
    return;
}

int main()
{
    while(scanf("%s",str) != EOF)
    {
        int len = strlen(str);
        if(str[0] == '#')
            break;
        for(int i = 0; i < len; i++)
            s[i] = str[i]-'a'+1;
        s[len] = 0;//这个赋值为0是关键所在
        get_sa(s,sa,len+1,27);
        get_height(s,sa,len);
        //for(int i=0;i<len+1;i++) printf("%d %d\n",i,sa[i]);
        int ans = 0;
        for(int i = 1; i <= (len+1)/2; i++)
        {
            //查一半就好了,长度大于(len+1)/2的子串不可能重复俩次啦
            //长度为i的重复子串
            int minn = N;
            int maxn = -1;
            for(int j = 1; j <= len; j++)
            {
                if(height[j] >= i)//连续
                {
                    minn = Min(minn,Min(sa[j-1],sa[j]));
                    maxn = Max(maxn,Max(sa[j-1],sa[j]));
                }
                else//若height的值不是连续大于i的
                {
                    if(minn+i <= maxn)
                        ans++;
                    minn = N, maxn = -1;
                }
            }
            if(minn+i <= maxn)
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

最后再贴两个讲解后缀数组很详细的链接:

1、http://wenku.baidu.com/link?url=g6BzfQTbVcypv0wSYhjw0dtlf6eKSp20Jm5KZDT_EzMKZec_tNGNCUU7lzUWcyC6BvhC73raG2E0Uda2lmykTkmGEuhaWKxJV6CA8K3ZzG_

2、http://www.cnblogs.com/staginner/archive/2012/02/02/2335600.html

时间: 2024-12-04 20:02:55

HDU 3518 Boring counting(后缀数组啊 求字符串中不重叠的重复出现至少两次的子串的个数)的相关文章

hdu 3518 Boring counting 后缀数组LCP

题目链接 题意:给定长度为n(n <= 1000)的只含小写字母的字符串,问字符串子串不重叠出现最少两次的不同子串个数; input: aaaa ababcabb aaaaaa # output 2 3 3 思路:套用后缀数组求解出sa数组和height数组,之后枚举后缀的公共前缀长度i,由于不能重叠,所以计数的是相邻height不满足LCP >= i的. 写写对后缀数组倍增算法的理解: 1.如果要sa数组对应的值也是1~n就需要在最后加上一个最小的且不出现的字符'#',里面y[]是利用sa数

hdu 3518 Boring counting 后缀数组 height分组

题目链接 题意 对于给定的字符串,求有多少个 不重叠的子串 出现次数 \(\geq 2\). 思路 枚举子串长度 \(len\),以此作为分界值来对 \(height\) 值进行划分. 显然,对于每一组,组内子串具有一个长度为 \(len\) 的公共前缀. 至于是否重叠,只需判断 \(sa_{max}-sa_{min}\geq len\). 对于组间,显然它们的公共前缀互不相同,所以答案即为\(\sum_{len}\sum_{group}\) Code #include <bits/stdc++

hdu 3518 Boring counting(后缀数组)

Boring counting                                                                       Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Problem Description 035 now faced a tough problem,his english teacher gives him

POJ 1743 后缀数组:求最长不重叠子串

数据:这题弄了好久,WA了数十发,现在还有个例子没过,可却A了,POJ 的数组也太弱了. 10 1 1 1 1 1 1 1 1 1 1 这组数据如果没有那个n-1<10判断的话,输入的竟然是5,我靠-- 思路:这个题目关键的地方有两个:第一,重复的子串一定可以看作是某两个后缀的公共前缀,第二,把题目转化成去判定对于任意的一个长度k,是否存在长度至少为k的不重叠的重复的子串. 转化成判定问题之后,就可以二分去解答了.在验证判定是否正确时,我们可以把相邻的所有不小于k的height[]看成一组,然后

后缀数组 --- HDU 3518 Boring counting

Boring counting Problem's Link:   http://acm.hdu.edu.cn/showproblem.php?pid=3518 Mean: 给你一个字符串,让你求出有多少个子串(无重叠)至少出现了两次. analyse: 后缀数组中height数组的运用,一般这个数组用得很少. 总体思路:分组统计的思想:将相同前缀的后缀分在一个组,然后对于1到len/2的每一个固定长度进行统计ans. 首先我们先求一遍后缀数组,并把height数组求出来.height数组代表的

[HDU]3518——Boring counting

zhan.jiang.ou now faced a tough problem,his english teacher quan.hong.chun gives him a string,which consists with n lower case letter,he must figure out how many substrings appear at least twice,moreover,such apearances can not overlap each other.Tak

POJ 1743 Musical Theme (后缀数组,求最长不重叠重复子串)

题意: 有N(1 <= N <=20000)个音符的序列来表示一首乐曲,每个音符都是1..88范围内的整数,现在要找一个重复的主题. "主题"是整个音符序列的一个子串,它需要满足如下条件: 1.长度至少为5个音符 2.在乐曲中重复出现(可能经过转调,"转调"的意思是主题序列中每个音符都被加上或减去了同一个整数值.) 3.重复出现的同一主题不能有公共部分. 思路:是要求最长不重叠重复的子串,如果没有不重叠的限制条件,那么height中的最大值即可 现在对于

HDOJ 题目3518 Boring counting(后缀数组,求不重叠重复次数最少为2的子串种类数)

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

HDOJ 3518 Boring counting

SAM基本操作 拓扑求每个节点的  最左出现left,最右出现right,出现了几次num ...... 对于每一个出现两次以上的节点,对其所对应的一串子串的长度范围 [fa->len+1,len] 和其最大间距 right-left比较 即可...... Boring counting Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s):