后最数组的应用【**】

19 篇文章 0 订阅
4 篇文章 0 订阅


Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1074    Accepted Submission(s): 412


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
 

Recommend
zhengfeng


提到后缀数组,其实最重要的还是3个数组,sa数组,r数组,height数组;(具体请看罗穗骞大牛的论文)
但是想了良久之后,发现在套完模板之后(即:求出height数组之后),就感到有点难了,不知道最后怎么统计了!
经提醒后,方知可以这样来做:
我们知道对于sa[i],sa[j](i<j)之间的最长公共前缀就等于min(height[i+1]....height[j]);
那么如果我们现在已知height[k]=L,那么必然会有sa[i],sa[j]的最长公共前缀<=L(i<k&&k<j);
所以此时我们就应该想到枚举重复子串的长度len,每次枚举的时候我们求出连续的height>=len,并且在这过程中不断地更新最左端和最右端;
当这段连续height都>=len的区间中的min+L<=max(意思是没有重叠的)时,我们就可以ans++了;
所以这道题也就ok了;
先套模板求出height数组,然后不断地枚举长度从1到(n+1)/2(其中n为字符串的长度);
期间在用上面所述方法求之。。。。
后缀数组真尼玛蛋疼这题不知道wa了多少次。。。还是看的别人的题解。。。。。
囧啊。。。。。
题目不是太会做。。。。。
AC代码(看别人的)
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <stdlib.h>
#define INF 10000000
using namespace std;
#include <stdio.h>
#include <string.h>
const int MAX=200010;
int wa[MAX],wb[MAX],wv[MAX],ws[MAX];
int sa[MAX];
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(char *r,int *sa,int n,int m)//注意此处的char*r有时可以改成int* r
{
    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<<=1,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 rank[MAX],height[MAX];
void calheight(char *r,int *sa,int n)
{
    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 ;
}

char str[MAX];



int slove(int k,int n){
    int mmax=0,mmin=INF,ans=0;
    for(int i=1;i<=n;i++){
        if(height[i]<k){
            if(mmax-mmin>=k)
                ans++;
            mmax=0;mmin=INF;
        }
        else{
            mmax=max(max(sa[i-1],sa[i]),mmax);
            mmin=min(min(sa[i-1],sa[i]),mmin);
        }
    }
    if(mmax-mmin>=k) ans++;
    return ans;
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%s",str)==1&&str[0]!='#')
    {
        //memset(sa,0,sizeof(sa));
        int len = strlen(str);


        int n = len+1;

        da(str,sa,n,'z' + 1);

        calheight(str,sa,n-1);

        int ans = 0;

        for(int i = 1;i<= (n-1)/2;++i)
        {
           ans += slove(i,n-1);
        }
        printf("%d\n",ans);

    }
    return 0;
}

相对而言更加简洁:
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <stdlib.h>
#define INF 10000000
using namespace std;
#include <stdio.h>
#include <string.h>
const int MAX=200010;
int wa[MAX],wb[MAX],wv[MAX],ws[MAX];
int sa[MAX];
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void da(char *r,int *sa,int n,int m)//注意此处的char*r有时可以改成int* r
{
    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<<=1,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 rank[MAX],height[MAX];
void calheight(char *r,int *sa,int n)
{
    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 ;
}

char str[MAX];

/*
先二分答案,把题目变成判定性问题:判断是否
存在两个长度为k 的子串是相同的,且不重叠。解决这个问题的关键还是利用
height 数组。把排序后的后缀分成若干组,其中每组的后缀之间的height 值都
不小于k。例如,字符串为“aabaaaab”,当k=2 时,后缀分成了4 组
容易看出,有希望成为最长公共前缀不小于k 的两个后缀一定在同一组。然
后对于每组后缀,只须判断每个后缀的sa 值的最大值和最小值之差是否不小于
k。如果有一组满足,则说明存在,否则不存在。整个做法的时间复杂度为
O(nlogn)。
*/

//起初看上述大牛的论文对分组并不是很了解。。后来看了其他人的题解有了一定的理解
//对于sa[i],sa[j]那么之间最长公共子序列应该是min(hieght[i+1],height[i+2]....height[j])
//那么通过枚举公共子序列的长度将height数组分组(并且注意一组内的height是连续的!!!!!)
//并且在这过程中不断地更新最左端和最右端
//当这段连续height都>=len的区间中的min+L<=max(意思是没有重叠的)时,我们就可以ans++了;
//当height[i]<k时就进行分好一组的讨论看是否mmax-mmin>=k
//然后从新开始分组
int slove(int k,int n){//k为枚举的长度
    int mmax=sa[0],mmin=sa[0],ans=0;
    for(int i=1;i<n;i++){
        if(height[i]<k){//
            if(mmax-mmin>=k)//
                ans++;
            mmax=sa[i];
            mmin=sa[i];
        }
        else{//如果height[i]>=k就分在一组。。。并且更新sa的最大最小值
            mmax=max(sa[i],mmax);
            mmin=min(sa[i],mmin);
        }
    }
    if(mmax-mmin>=k) ans++;
    return ans;
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%s",str)==1&&str[0]!='#')
    {
        //memset(sa,0,sizeof(sa));
        int len = strlen(str);


        int n = len+1;

        da(str,sa,n,'z' + 1);

        calheight(str,sa,n-1);

        int ans = 0;

        for(int i = 1;i<= (n-1)/2;++i)
        {
           ans += slove(i,n);
        }
        printf("%d\n",ans);

    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值