POJ 3415 Common Substrings (求长度不小于k的公共子串的个数)

Common Substrings
Time Limit: 5000MS   Memory Limit: 65536K
Total Submissions: 10002   Accepted: 3302

Description

A substring of a string T is defined as:

 

T(ik)=TiTi+1...Ti+k-1, 1≤ii+k-1≤|T|.

 

Given two strings AB and one integer K, we define S, a set of triples (ijk):

 

S = {(ijk) | kKA(ik)=B(jk)}.

 

You are to give the value of |S| for specific AB and K.

Input

The input file contains several blocks of data. For each block, the first line contains one integer K, followed by two lines containing strings A and B, respectively. The input file is ended by K=0.

1 ≤ |A|, |B| ≤ 105
1 ≤ K ≤ min{|A|, |B|}
Characters of A and B are all Latin letters.

 

Output

For each case, output an integer |S|.

Sample Input

2
aababaa
abaabaa
1
xx
xx
0

Sample Output

22
5
/*
 * POJ 3415 Common Substrings
 * 给出两个字符串A,B,求长度不小于k的公共子串的个数
 *
 * 将B接到A的后面,中间用一个没出现的字符隔开,然后求sa和height数组
 * 然后按height分组,我们只考虑height[i]>=k的后缀,因为小于不符合题意,然后对于每个分组,统计答案
 * 很容易想到算法就是对于同一组里面的B后缀,我和前面的所有A的后缀比较,累加答案,再反过来对A做一遍即可
 * 这样的时间复杂度是O(n*n)的,会TLE
 * 考虑这样的一个性质,后缀i和j的lcp是height[rank[i+1]]...height[rank[j]]的最小值,那么对于
 * height[i]==s&&sa[i]是B的后缀,它和前面A的答案肯定是最小的height的累加,于是这样我们可以用一个
 * 单调栈来维护,每当新加入的height小于栈顶的元素,我们就把栈顶元素弹出来,把新的值压入栈中
 * 怎么统计答案呢?可以先加后减,当计算B的个数时,遇到A的后缀我们就直接累加到答案中,如果和栈顶元素冲突了
 * 说明多加了一部分,在弹出元素时减去即可,这样多维护一个个数就行了。
 */

#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;

const int MAXN = 200000+100;

int sa[MAXN];
int t1[MAXN],t2[MAXN],c[MAXN];
int Rank[MAXN],height[MAXN];
void build_sa(int s[],int n,int m)
{
    int i,j,p,*x=t1,*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(j=1;j<=n;j<<=1)
    {
        p=0;
        for(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<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[y[i]]]++;
        for(i=1;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-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        if(p>=n)break;
        m=p;
    }
}
void getHeight(int s[],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[i+k]==s[j+k])k++;
        height[Rank[i]]=k;
    }
}
char str1[MAXN],str2[MAXN];
int ss[MAXN];
int sta[MAXN],stb[MAXN];

int main()
{
    int k;
    while(scanf("%d",&k)==1&&k)
    {
        scanf("%s",str1);
        scanf("%s",str2);
        int len1=strlen(str1);
        int len2=strlen(str2);
        int n=len1+len2+1;
        for(int i=0;i<len1;i++) ss[i]=str1[i];
        ss[len1]=1;
        for(int i=0;i<len2;i++) ss[len1+i+1]=str2[i];
        ss[n]=0;
        build_sa(ss,n+1,128);
        getHeight(ss,n);
        long long ans=0;
        long long res=0;
        int top=0;
        for(int i=2;i<=n;i++)
        {
            if(height[i]<k)
            {
                res=0;
                top=0;
                continue;
            }
            int cnt=0;
            if(sa[i-1]<len1)
            {
                cnt++;
                res+=height[i]-k+1;
            }
            while(top>0&&height[i]<=sta[top-1])
            {
                top--;
                res-=stb[top]*(sta[top]-height[i]);
                cnt+=stb[top];
            }
            sta[top]=height[i],stb[top++]=cnt;
            if(sa[i]>len1) ans+=res;
        }
        res=0,top=0;
        for(int i=2;i<=n;i++)
        {
            if(height[i]<k)
            {
                res=0;
                top=0;
                continue;
            }
            int cnt=0;
            if(sa[i-1]>len1)
            {
                cnt++;
                res+=height[i]-k+1;
            }
            while(top>0&&height[i]<=sta[top-1])
            {
                top--;
                res-=stb[top]*(sta[top]-height[i]);
                cnt+=stb[top];
            }
            sta[top]=height[i],stb[top++]=cnt;
            if(sa[i]<len1) ans+=res;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2016-08-18 22:10  季末Despair  阅读(215)  评论(0编辑  收藏  举报