【题目链接】

 

  http://poj.org/problem?id=3415

 

【题意】

 

  A与B长度至少为k的公共子串个数。

 

【思路】

  

  基本思想是将AB各个后缀的lcp-k+1的值求和。首先将两个字符串拼接起来中间用未出现的字符隔开,划分height数组,这首先保证了每一组中字符串之间的公共子串至少有k长度,组与组之间互不干扰。

  问题变成了求一个组中一个A串与之前B串形成的LCP(lcp-k+1)和一个B串与之前A串形成的LCP,问题是对称的,这里先解决第一个。用一个单调栈,栈中存放两个元素分别height_top与cnt_top,分别表示到i为止的最小height和A串的数目。维护栈中元素的height从顶到底递减:每加入一个元素如果该元素比栈顶元素小则需要将tot中cnt_top个已经累计的height_top全部替换为当前元素的height(lcp是取区间最小值)。

     时间复杂度为O(n)。

 

【代码】

 

#include<cstdio>
#include<cstring>
#include<iostream>
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std;

typedef long long LL;
const int maxn = 400000 + 10;

int s[maxn];
int sa[maxn],c[maxn],t[maxn],t2[maxn];

void build_sa(int m,int n) {
    int i,*x=t,*y=t2;
    for(i=0;i<m;i++) c[i]=0;
    for(i=0;i<n;i++) c[x[i]=s[i]]++;
    for(i=1;i<m;i++) c[i]+=c[i-1];
    for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1) {
        int p=0;
        for(i=n-k;i<n;i++) y[p++]=i;
        for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
        for(i=0;i<m;i++) c[i]=0;
        for(i=0;i<n;i++) c[x[y[i]]]++;
        for(i=0;i<m;i++) c[i]+=c[i-1];
        for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1; x[sa[0]]=0;
        for(i=1;i<n;i++) 
            x[sa[i]]=y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
        if(p>=n) break;
        m=p;
    }
}
int rank[maxn],height[maxn];
void getHeight(int n) {
    int i,j,k=0;
    for(i=0;i<=n;i++) rank[sa[i]]=i;
    for(i=0;i<n;i++) {  
        if(k) k--;
        j=sa[rank[i]-1];
        while(s[j+k]==s[i+k]) k++;
        height[rank[i]]=k;
    }
}

int n,k;
char a[maxn],b[maxn];
int sta[maxn][2];

int main() {
    while(scanf("%d",&k)==1 && k) {
        scanf("%s%s",a,b);
        int lena=strlen(a),lenb=strlen(b);
        n=0;
        for(int i=0;i<lena;i++) s[n++]=a[i];
        s[n++]=1;
        for(int i=0;i<lenb;i++) s[n++]=b[i];
        s[n]=0;
        
        build_sa('z'+1,n+1);
        getHeight(n);
        
        int top=0;
        LL ans=0,tot=0;
        for(int i=1;i<=n;i++) {
            if(height[i]<k) top=0,tot=0;
            else {
                int cnt=0;
                if(sa[i-1]<lena) {
                    cnt++; tot+=height[i]-k+1;
                }
                while(top && height[i]<=sta[top-1][0]) {
                    top--;
                    tot+=(height[i]-sta[top][0])*sta[top][1];
                    cnt+=sta[top][1];
                }
                sta[top][0]=height[i],sta[top++][1]=cnt;
                if(sa[i]>lena) ans+=tot;
            }
        }
        top=tot=0;
        for(int i=1;i<=n;i++) {
            if(height[i]<k) top=0,tot=0;
            else {
                int cnt=0;
                if(sa[i-1]>lena) {
                    cnt++; tot+=height[i]-k+1;
                }
                while(top && height[i]<=sta[top-1][0]) {
                    top--;
                    tot+=(height[i]-sta[top][0])*sta[top][1];
                    cnt+=sta[top][1];
                }
                sta[top][0]=height[i],sta[top++][1]=cnt;
                if(sa[i]<lena) ans+=tot;
            }
        }
        cout<<ans<<"\n";
    }
    return 0;
}
View Code

相关文章:

  • 2022-12-23
  • 2021-11-10
  • 2021-10-10
  • 2021-12-02
  • 2021-09-13
  • 2022-12-23
  • 2022-02-16
猜你喜欢
  • 2021-07-17
  • 2022-12-23
  • 2021-10-02
  • 2022-12-23
  • 2021-12-23
  • 2021-12-08
  • 2022-12-23
相关资源
相似解决方案