HDU 4029 Distinct Sub-matrix(后缀数组)

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

题意:给定一个n*m的字母矩阵。求有多少个不同的子矩阵?

思路:枚举列数w,hash[i][j]保存第i行从第j列开始宽度为w的串的哈希值,然后以列为串,也就是有m-w+1个串建立自动机。求不同子串就是宽度为w时的不同的矩阵个数。





int r[N],sa[N],wa[N],wb[N],wd[N],rank[N],h[N];



int cmp(int *r,int a,int b,int len)

{

    return r[a]==r[b]&&r[a+len]==r[b+len];

}



void da(int *r,int *sa,int n,int m)

{

    int i,j,p,*x=wa,*y=wb,*t;

    FOR0(i,m) wd[i]=0;

    FOR0(i,n) wd[x[i]=r[i]]++;

    FOR1(i,m-1) wd[i]+=wd[i-1];

    FORL0(i,n-1) sa[--wd[x[i]]]=i;

    for(j=1,p=1;p<n;j<<=1,m=p)

    {

        p=0;

        FOR(i,n-j,n-1) y[p++]=i;

        FOR0(i,n) if(sa[i]>=j) y[p++]=sa[i]-j;

        FOR0(i,m) wd[i]=0;

        FOR0(i,n) wd[x[i]]++;

        FOR1(i,m-1) wd[i]+=wd[i-1];

        FORL0(i,n-1) sa[--wd[x[y[i]]]]=y[i];

        t=x;x=y;y=t;p=1;x[sa[0]]=0;

        FOR1(i,n-1) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;

    }

}







void calHeight(int *r,int *sa,int n)

{

    int i,j,k=0;

    FOR1(i,n) rank[sa[i]]=i;

    FOR0(i,n)

    {

        if(k) k--;

        j=sa[rank[i]-1];

        while(i+k<n&&j+k<n&&r[i+k]==r[j+k]) k++;

        h[rank[i]]=k;

    }

}





int n,m,b[N];

char s[N],s1[140][140];

u64 hash[140][140];

map<u64,int> mp;

int Max;



void init(int w)

{

    mp.clear(); Max=0;

    int i,j,k;

    FOR0(i,n) FOR0(j,m-w+1)

    {

        hash[i][j]=0;

        for(k=j;k<j+w;k++)

        {

            hash[i][j]=hash[i][j]*107+s1[i][k];

        }

        if(mp.find(hash[i][j])==mp.end())

        {

            mp[hash[i][j]]=++Max;

        }

    }

}



int M;



u64 getCnt(int colCnt)

{

    u64 ans=(i64)M*(M+1)/2;

    int i;

    FOR1(i,M) ans-=h[i];

    FOR1(i,colCnt)

    {

        M-=n;

        ans-=(i64)(n+1)*M;

        M--;

    }

    return ans;

}



u64 cal(int w)

{

    init(w);

    int L=0,i,j;

    FOR0(j,m-w+1)

    {

        FOR0(i,n) r[L++]=mp[hash[i][j]];

        if(j<m-w) r[L++]=++Max;

    }

    r[L]=0;

    da(r,sa,L+1,Max+1);

    calHeight(r,sa,L);

    M=L;

    u64 temp=getCnt(m-w+1);

    return temp;

}





int main()

{

    int C,num=0;

    RD(C);

    while(C--)

    {

        RD(n,m);

        int i;

        u64 ans=0;

        FOR0(i,n) RD(s1[i]);

        FOR1(i,m) ans+=cal(i);

        printf("Case #%d: %I64u\n",++num,ans);

    }

    return 0;

}

  

你可能感兴趣的:(distinct)