poj3415Common Substrings(好题)

来源:互联网 发布:免费开源网管软件 编辑:程序博客网 时间:2024/05/06 12:28
Common Substrings
Time Limit: 5000MS Memory Limit: 65536KTotal Submissions: 8519 Accepted: 2812

Description

A substring of a string T is defined as:

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

Given two strings A, B and one integer K, we defineS, a set of triples (i, j, k):

S = {(i, j, k) | kK,A(i, k)=B(j, k)}.

You are to give the value of |S| for specific A, B andK.

Input

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

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

Output

For each case, output an integer |S|.

Sample Input

2aababaaabaabaa1xxxx0

Sample Output

225

Source

POJ Monthly--2007.10.06, wintokk

论文题,
基本思路是计算 A 的所有后缀和 B 的所有后缀之间的最长公共前缀的长度,
把最长公共前缀长度不小于 k 的部分全部加起来。先将两个字符串连起来,中间
用一个没有出现过的字符隔开。按 height 值分组后,接下来的工作便是快速的
统计每组中后缀之间的最长公共前缀之和。扫描一遍,每遇到一个 B 的后缀就统
计与前面的 A 的后缀能产生多少个长度不小于 k 的公共子串, 这里 A 的后缀需要
用一个单调的栈来高效的维护。然后对 A 也这样做一次。(摘自论文)
#include <map>#include <set>#include <stack>#include <queue>#include <cmath>#include <ctime>#include <vector>#include <cstdio>#include <cctype>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;#define INF 0x3f3f3f3f#define inf -0x3f3f3f3f#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define mem0(a) memset(a,0,sizeof(a))#define mem1(a) memset(a,-1,sizeof(a))#define mem(a, b) memset(a, b, sizeof(a))typedef long long ll;const int maxn=200000+100;char s[maxn],s1[maxn];int st[maxn][2];int sa[maxn],t[maxn],t2[maxn],c[maxn],n;//构造字符串s的后缀数组,每个字符值必须为0~m-1void build_sa(int m){    int *x=t,*y=t2;    //基数排序    for(int i=0;i<m;i++)    c[i]=0;    for(int i=0;i<n;i++)    c[x[i]=s[i]]++;    for(int i=1;i<m;i++)    c[i]+=c[i-1];    for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;    for(int k=1;k<=n;k<<=1){        int p=0;        //直接利用sa数组排序第二关键字        for(int i=n-k;i<n;i++)  y[p++]=i;        for(int i=0;i<n;i++)    if(sa[i]>=k)    y[p++]=sa[i]-k;        //基数排序第一关键字        for(int i=0;i<m;i++)    c[i]=0;        for(int i=0;i<n;i++)    c[x[y[i]]]++;        for(int i=1;i<m;i++)    c[i]+=c[i-1];        for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];        //根据sa和y计算新的x数组        swap(x,y);        p=1;        x[sa[0]]=0;        for(int i=1;i<n;i++)            x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;        if(p>=n)            break;        m=p;                //下次基数排序的最大值    }}int rank1[maxn],height[maxn];void getHeight(){    int i,j,k=0;    for(i=0;i<=n;i++)    rank1[sa[i]]=i;    for(i=0;i<n;i++){        if(k)            k--;        int j=sa[rank1[i]-1];        while(s[i+k]==s[j+k])   k++;        height[rank1[i]]=k;    }}int main(){    int k;    while(scanf("%d",&k)!=EOF){        if(k==0)            break;        scanf("%s%s",&s,&s1);        int l1=strlen(s);        s[l1]='&';        s[l1+1]='\0';        strcat(s,s1);        n=strlen(s);        s[n++]='0';        build_sa(128);        n--;        getHeight();        ll sum=0;        ll tot=0,top=0;        for(int i=1;i<=n;i++){  //表示排名            int cnt=0;            if(height[i]<k){                tot=0;                top=0;            }            else{                if(sa[i-1]<l1){                    tot+=(height[i]-k+1);                    cnt++;                }                while(top>0&&height[i]<=st[top-1][0]){                    top--;                    tot-=st[top][1]*(st[top][0]-height[i]);                    cnt+=st[top][1];                }                st[top][0]=height[i];                st[top++][1]=cnt;   //st[top][1]表示这个区间内含有多少个sa小于l1的                if(sa[i]>l1)                    sum+=tot;            }        }        tot=0,top=0;        for(int i=1;i<=n;i++){  //表示排名            int cnt=0;            if(height[i]<k){                tot=0;                top=0;            }            else{                if(sa[i-1]>l1){                    tot+=(height[i]-k+1);                    cnt++;                }                while(top>0&&height[i]<=st[top-1][0]){                    top--;                    tot-=st[top][1]*(st[top][0]-height[i]);                    cnt+=st[top][1];                }                st[top][0]=height[i];                st[top++][1]=cnt;                if(sa[i]<l1)                    sum+=tot;            }        }        printf("%I64d\n",sum);    }    return 0;}


0 0
原创粉丝点击