现在的位置: 首页 > 综合 > 正文

hdu4416 Good Article Good sentence (后缀数组)

2012年01月07日 ⁄ 综合 ⁄ 共 2078字 ⁄ 字号 评论关闭

题意:求A 串中不在 所有B串中出现大不同子串的个数

分析:后缀数组可以很简单的实现求一个串不同字子串的个数,,从这方面下手就很容易想到了,之前比赛的时候,,死活没想法

先将所有串拼接起来,计算不包含分隔符的不同子串的个数sumAB,再单独将所有B串拼接起来,同样的方法求一遍,得到sumB,sumAB-sumB即为所求

View Code

#include <iostream>
#include <algorithm>
#include <string.h> 
#include <math.h>
#include <string>

int const N= 400100;    
int wa[N], wb[N], ws[N*2], wv[N];    
int rank[N], sa[N], height[N], r[N];    
char str[N];

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];    
    
        t= x, x= y, y= t, p= 1; x[ sa[0] ]= 0;    
        for( i= 1; i< n; ++i )    
            x[ sa[i] ]= cmp( y, sa[i-1], sa[i], j )? p- 1: p++;    
    }    
}    
    
void callheight( int* 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++ );    
    
}

int s[N],length[N],id[N];
//s[]表示每一个串的在拼接后在串中的起始下标,length[]表示每一个串的长度,id[]记录拼接后每一个下标对应的原串的id
__int64 get_num(int n)//计算不包含分隔符的不同子串的个数
{
    __int64 sum=0;
    for(int i=1;i<=n;++i)
    {
        if(id[sa[i]]<0) continue;
        sum+=length[id[sa[i]]]-(sa[i]-s[id[sa[i]]])-height[i];
        //拼接后的串的计算子串的方法跟单个串的时候差不多,,原来是一样的,,只不过不能忽视分隔符的存在
    }
    return sum;
}
void print(int *s,int n)
{
    for(int i=0;i<n;++i)
        printf("%d ",s[i]);
    printf("\n");
}
int main()
{
    int T,cas=0;
    int n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        memset(id,-1,sizeof(id));
        int len=-1,m=27;
        for(int i=0;i<=n;++i)
        {
            ++len;
            scanf("%s",str);
            length[i]=strlen(str);
            s[i]=len;
            for(int j=0;j<length[i];++j)
            {
                id[len]=i;
                r[len++]=str[j]-'a'+1;
            }
            r[len]=m++;
        }
        r[len]=0;
        da(r,sa,len+1,m);
        callheight(r,sa,len);
        __int64 ans1=get_num(len);//A串 + B 串中所有串可以形成的总的不同子串的个数
        for(int i=1;i<=n;++i)
            s[i]=s[i]-length[0]-1;
        for(int i=0;i<=len-length[0]-1;++i)
        {
            id[i]=id[i+length[0]+1];
            r[i]=r[i+length[0]+1];
        }
        len=len-length[0]-1;
        da(r,sa,len+1,m);
        callheight(r,sa,len);
        __int64 ans2=get_num(len);//B中所有串可以形成的总的子串的个数
        printf("Case %d: %I64d\n",++cas,ans1-ans2);
    }
    return 0;
}

 

抱歉!评论已关闭.