spoj Distinct Substrings&&hdoj5769 Substrings(后缀数组求不同子串个数)

 spoj

  题目大意:给一个字符串s,输出其不同子串的个数。|s|<=50000

  思路分析:直接暴力枚举子串去重 n^3直接爆。后缀数组可求height数组,然后第sa[i]个后缀包含的不同子串为n-height[i]-sa                    [i],遍历一遍叠加即可,求后缀数组为nlog(n).

代码:

       

#include<iostream>
#include<string.h>
#include<math.h>
#include<stdio.h>
using namespace std;
char s[100010];
int sa[100010],c[100010],x[100010],y[100010],r[100010],h[100010];
void build(int n,int m){
    int i,j,k;
    for(i=0;i<m;i++) c[i]=0;
    for(i=0;i<n;i++){
        x[i]=s[i];
        c[x[i]]++;
    }
    for(i=1;i<m;i++) c[i]+=c[i-1];
    for(i=0;i<n;i++) sa[--c[x[i]]]=i;
    for(j=1;j<n;j*=2){
        k=0;
        for(i=n-j;i<n;i++) y[k++]=i;
        for(i=0;i<n;i++) if(sa[i]>=j) y[k++]=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);
        m=1;
        x[sa[0]]=0;
        for(i=1;i<n;i++){
            if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j]) x[sa[i]]=m-1;
            else x[sa[i]]=m++;
        }
        if(m>=n) break;
    }
}
void height(int n){
    int i,j,k=0;
    for(i=0;i<=n;i++) r[sa[i]]=i;
    for(i=0;i<n;i++){
        if(k) k--;
        j=sa[r[i]-1];
        while(s[i+k]==s[j+k]) k++;
        h[r[i]]=k;
    }
}
int main(){
    int i,t,n;
    scanf("%d",&t);
    while(t--){
        scanf("%s",&s);
        n=strlen(s);
        s[n]=0;
        build(n+1,2000);
        height(n);
        long long sum=0;
        for(i=1;i<=n;i++){
            sum+=n-h[i]-sa[i];
        }
        cout<<sum<<endl;
    }
    return 0;
}


hdoj 5769

题目大意:给字符串s,字符x,求包含字符x的不同子串的个数。|s|<=100000

思路分析:大致思路同上,只是求不同子串时要考虑第一个出现在s[i]后x的位置,先处理x的位置再二分一下。。。

代码:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
char s[100010],t[10];
int sa[100010],c[100010],x[100010],y[100010],r[100010],h[100010];
void build(int n){
    int i,j,k,p,m=30;
    for(i=0;i<m;i++) c[i]=0;
    for(i=0;i<n;i++){
        x[i]=s[i]-'a'+1;
        c[x[i]]++;
    }
    for(i=1;i<m;i++) c[i]+=c[i-1];
    for(i=0;i<n;i++) sa[--c[x[i]]]=i;
    for(j=1;j<n;j*=2){
        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);
        m=0;
        x[sa[0]]=m++;
        for(i=1;i<n;i++){
            if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])  x[sa[i]]=m-1;
            else x[sa[i]]=m++;
        }
        if(m>=n) break;
    }
}
void height(int n){
    int i,j,k=0;
    for(i=1;i<=n;i++) r[sa[i]]=i;
    for(i=0;i<n;i++){
        if(k) k--;
        j=sa[r[i]-1];
        while(s[i+k]==s[j+k]) k++;
        h[r[i]]=k;
    }
}
int A[100010];
int main(){
    int T,i,n,k,j=0;
    scanf("%d",&T);
    while(T--){
        j++;
        scanf("%s",&t);
        scanf("%s",&s);
        n=strlen(s);
        s[n]='a'-1;
        build(n+1);
        height(n);
        //for(i=1;i<=n;i++) cout<<h[i]<<endl;
        k=0;
        for(i=0;i<n;i++) if(s[i]==t[0]) A[k++]=i;
        long long a,sum=0;
        for(i=1;i<=n;i++){
            a=lower_bound(A,A+k,sa[i])-A;
            if(a==k) continue;  //木有找到sa[i]后的x
            a=A[a];
            if(a<=sa[i]+h[i]-1) a=sa[i]+h[i];    //x出现在公共前缀部分
            sum+=n-a;
        }
        cout<<"Case #"<<j<<": "<<sum<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/guogai13/article/details/80992284