您的位置:首页 > Web前端

POJ 3415 Life Forms 给定n个字符串,求出现在不小于k个字符串中的最长子串。

2017-10-01 17:17 441 查看


给定两个字符串A和B,求长度不小于k的公共子串的个数(可以相同)


这个题要用到手工模拟的单调栈。首先这个题如果暴力应该怎么解决。例如:

2

aaa

Aaaa

 

                                          中间加个z

他的高度数组应该是

0                                 ‘\0’

0                                   a

1                                   aa

2                                   aaa

3                                   aaaa

3                                   aaazaaaa

2                                   aazaaaa

1                                   azaaaa

0                                   zaaaa

前缀大于2的在一个字符串里面的不能计算我们从上面可以看出

aaazaaaa和aa,aaa,aaaa分别进行匹配,aazaaaa和aa,aaa,aaaa分别进行匹配

前缀。一共是8个。如果进行暴力,应该是接近n*n不进行暴力,手动模拟单调栈,O(n)的算法。就是看当前串和前一个串能匹配多少个。不太好写具体怎么一步步走的,只能看着代码自己理解了。

#include<iostream>  
#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#include<cmath>  
#define N 100005  
#define LL long long  
#define maxn 200005  
using namespace std;  
//以下为倍增算法求后缀数组    
int wa[maxn],wb[maxn],wv[maxn],Ws[maxn];    
int cmp(int *r,int a,int b,int l)    
{return r[a]==r[b]&&r[a+l]==r[b+l];}    
void da(const char *r,int *sa,int n,int m){    
    int i,j,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(j=1,p=1;p<n;j*=2,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,x[sa[0]]=0,i=1;i<n;i++)     
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;     
    }     
    return;     
}    
int sa[maxn],Rank[maxn],height[maxn];    
//求height数组    
void calheight(const char *r,int *sa,int n){    
    int i,j,k=0;    
    for(i=1;i<=n;i++) Rank[sa[i]]=i;    
    for(i=0;i<n;height[Rank[i++]]=k)    
        for(k?k--:0,j=sa[Rank[i]-1];r[i+k]==r[j+k];k++);    
    return;    
}  
char str[maxn],ch[maxn];  
int k;  
int s[maxn][2];  
LL tot,top;  
int main(){  
    while(scanf("%d",&k)!=EOF&&k){  
        int l1,l2;  
        scanf("%s%s",str,ch);  
        l1=strlen(str);l2=strlen(ch);  
        str[l1]='@';  
        for(int i=l1+1;i<=l1+l2;i++)  
            str[i]=ch[i-l1-1];  
        int n=l1+l2+1;  
        str
='\0';  
        da(str,sa,n+1,130);  
        calheight(str,sa,n);  
        tot=top=0;  
        LL sum=0;  
        for(int i=1;i<=n;i++){  
            if(height[i]<k) top=tot=0;  
            else{  
                int cnt=0;  
                if(sa[i-1]<l1) cnt++,tot+=height[i]-k+1;  
                while(top>0&&height[i]<=s[top-1][0]){  
                    top--;  
                    tot-=s[top][1]*(s[top][0]-height[i]);  
                    cnt+=s[top][1];  
                }  
                s[top][0]=height[i];s[top++][1]=cnt;  
                if(sa[i]>l1) sum+=tot;  
            }  
        }  
        tot=top=0;  
        for(int i=1;i<=n;i++){  
            if(height[i]<k) top=tot=0;  
            else{  
                int cnt=0;  
                if(sa[i-1]>l1) cnt++,tot+=height[i]-k+1;  
                while(top>0&&height[i]<=s[top-1][0]){  
                    top--;  
                    tot-=s[top][1]*(s[top][0]-height[i]);  
                    cnt+=s[top][1];  
                }  
                s[top][0]=height[i];s[top++][1]=cnt;  
                if(sa[i]<l1) sum+=tot;  
            }  
        }  
        printf("%I64d\n",sum);  
    }  
    return 0;  
}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: