POJ-3415-Common-Substring-后缀数组-并查集

题目

题目链接

内容

给两个字符串 a , b a,b ,和整数 k k 询问 a , b a,b 的公共子串中长度大于等于 k k 的个数

分析

对于一个长度为 l e n ( l e n > = k ) len(len>=k) 的公共子串,它对答案的贡献为 l e n k + 1 len-k+1 ,

我们构建后缀数组,将 h e i g h t height 数组排序,从大到小遍历 h e i g h t height ,每次合并一个区间.

期间用并查集维护区间信息

void Union(int a,int b)
{
    int faa=Find(a),fab=Find(b);
    numa[faa]+=numa[fab];
    numb[faa]+=numb[fab];
    fa[fab]=faa;
}

n u m a numa 为属于 a a 的后缀个数, n u m b numb 同理.

每次合并之前,计算被合并的 两个区间对答案的贡献.

即:

  num=numa[l]*numb[r]+numa[r]*numb[l];
  ans+=(len-k+1)*num;

n u m num 为长度大于k公共子串的数目.
由于我们是从大到小遍历 h e i g h t height 数组的,所以 l e n len 为当遍历到的 h e i g h t height

代码

#include <iostream>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define pb push_back
#define fi first
#define se second
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
const int maxn=2e5+10;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
const int maxbit=20;
struct SuffixArray
{
    int sa[maxn], rank[maxn], ws[maxn], wv[maxn], wa[maxn], wb[maxn], height[maxn], st[maxbit][maxn], N;
    bool cmp(int *r, int a, int b, int l){return r[a]==r[b] and r[a+l]==r[b+l];}
    void build(int *r, int n, int m)
    {
        N=n;
        n++;
        int i, j, k=0, 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(p=j=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,i=1,x[sa[0]]=0;i<n;i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
        
        for(i=0;i<n;i++)rank[sa[i]]=i;
        
        for(i=0;i<n-1;height[rank[i++]]=k)
            for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
    }
    void build_st()     //st表
    {
        int i, k;
        for(i=1;i<=N;i++)st[0][i]=height[i];
        for(k=1;k<=maxbit;k++)
            for(i=1;i+(1<<k)-1<=N;i++)
                st[k][i]=min(st[k-1][i],st[k-1][i+(1<<k-1)]);
    }
    int lcp(int x, int y)   //最长公共前缀
    {
        int l=rank[x], r=rank[y];
        if(l>r)swap(l,r);
        if(l==r)return N-sa[l];
        int t=log2(r-l);
        return min(st[t][l+1],st[t][r-(1<<t)+1]);
    }
}SA;
int strcp[maxn],k,n,fa[maxn],numa[maxn],numb[maxn];
int Find(int x)
{
    if(fa[x]==x) return x;
    else return fa[x]=Find(fa[x]);
}
void Union(int a,int b)
{
    int faa=Find(a),fab=Find(b);
    numa[faa]+=numa[fab];
    numb[faa]+=numb[fab];
    fa[fab]=faa;
}
pii pos[maxn];
int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    while(1)
    {
        cin>>k;
        if(k==0) break;
        string a,b;
        cin>>a>>b;
        int lena=a.size(),lenb=b.size();
        a+='#';a+=b;
        n=a.size();
        rep(i,0,n-1) strcp[i]=a[i];
        rep(i,0,n)
        {
            fa[i]=i;
            numa[i]=numb[i]=0;
        }
        strcp[n]=0;
        SA.build(strcp,n,300);
        rep(i,1,n)
        {
           if(SA.sa[i]<lena) numa[i]++;
           else if(SA.sa[i]>lena) numb[i]++;
        }
        rep(i,2,n) pos[i-1].fi=SA.height[i],pos[i-1].se=i;
        ll ans=0;
        sort(pos+1,pos+n);
        per(i,n-1,1)
        {
            if(pos[i].fi<k) break;
            int l=Find(pos[i].se-1),r=Find(pos[i].se);
            ll num=numa[l]*numb[r]+numa[r]*numb[l];
            ans+=(pos[i].fi-k+1)*num;
            Union(l,r);
        }
        cout<<ans<<'\n';
    }
    return 0;
}


发布了10 篇原创文章 · 获赞 0 · 访问量 330

猜你喜欢

转载自blog.csdn.net/WA_King/article/details/103819557