后缀数组学习和经典题

模板

其中 build_sa,getheight, st_build, query的部分都是将原字符串经过最后一个字符为0的处理后的模板

倍增和D3C两个版本 倍增是O(n)log(n)   D3C接近 O(n)

倍增版本

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1200000;  //字符总长度加上连接字符的个数
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn],dp[maxn][25];
char str1[maxn];
int lens[maxn],belong[maxn];
int len;
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int n,int m)
{
    int *x=t1,*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;
    int p;
    for(int k=1;k<=n;k<<=1,m=p)
    {
        p=0;
        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];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int i=1;i<n;i++)
        {
            x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
        }
        if(p>=n)break;
    }
}
void getheight(int n)
{
    int k=0;
    for(int i=0;i<n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])
        {
            k++;
        }
        height[rk[i]]=k;
    }
}

void st_build(int n)
{
    for(int i = 0; i < n; i++)
    {
        dp[i][0] = height[i];
    }
    for(int j = 1; (1<<j) <= n; j++)
        for(int i = 0; (i+(1<<j)-1) <= n; i++)
            dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}

int query(int i, int j)
{
    int l = min(rk[i], rk[j]);
    int r = max(rk[i], rk[j]);
    ++l;
    int cnt = log2(r-l+1), len  = 1<<cnt;
    return min(dp[l][cnt], dp[r-len+1][cnt]);
}
int main()
{
        build_sa(n+1,100500);
        getheight(n+1);
        st_build(n+1);
        int l,r;
        query(l,r); //l,r为下标
        return 0;
}

D3C版本:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define F(x) ((x)/3+((x)%3==1?0:tb))
#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
#define ll long long
using namespace std;
const int maxn=3200000;  // d3c要开 三倍的空间 还要额外加上连接字符的长度
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn],dp[maxn][25];
char str1[maxn];
int lens[maxn],belong[maxn];
int len;
int wa[maxn],wb[maxn],wv[maxn],wws[maxn];
void sort(int *r,int *a,int *b,int n,int m)
{
      int i;
      for(i=0;i<n;i++) wv[i]=r[a[i]];
      for(i=0;i<m;i++) wws[i]=0;
      for(i=0;i<n;i++) wws[wv[i]]++;
      for(i=1;i<m;i++) wws[i]+=wws[i-1];
      for(i=n-1;i>=0;i--) b[--wws[wv[i]]]=a[i];
     return;
}
int c0(int *r,int a,int b)
{
    return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];
}
int c12(int k,int *r,int a,int b)
{
    if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1);
    else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];
}

void dc3(int *r,int *sa,int n,int m)
{
    int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p;
    r[n]=r[n+1]=0;
    for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i;
    sort(r+2,wa,wb,tbc,m);
    sort(r+1,wb,wa,tbc,m);
    sort(r,wa,wb,tbc,m);
    for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++)
          rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++;
    if(p<tbc) dc3(rn,san,tbc,p);
          else for(i=0;i<tbc;i++) san[rn[i]]=i;
    for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3;
    if(n%3==1) wb[ta++]=n-1;
    sort(r,wb,wa,ta,m);
    for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i;
    for(i=0,j=0,p=0;i<ta && j<tbc;p++)
          sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++];
    for(;i<ta;p++) sa[p]=wa[i++];
    for(;j<tbc;p++) sa[p]=wb[j++];
    return;
}
void getheight(int n)
{
    int k=0;
    for(int i=0;i<n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])
        {
            k++;
        }
        height[rk[i]]=k;
    }
}
void st_build(int n)
{
    for(int i = 0; i < n; i++)
    {
        dp[i][0] = height[i];
    }
    for(int j = 1; (1<<j) <= n; j++)
        for(int i = 0; (i+(1<<j)-1) <= n; i++)
            dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}

int query(int i, int j)
{
    int l = min(rk[i], rk[j]);
    int r = max(rk[i], rk[j]);
    ++l;
    int cnt = log2(r-l+1), len  = 1<<cnt;
    return min(dp[l][cnt], dp[r-len+1][cnt]);
}
int main()
{
        dc3(s, sa, n+1,100500);
        getheight(n+1);
        st_build(n+1);
        int l,r;
        query(l,r);
        return 0;
}

第一题:

POJ2774

求两个串的最长公共子串,将两个串拼接在一起,然后求height数组 遍历一遍height数组如果sa[i-1]和sa[i]同时属于两个串

则更新答案

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=807000;
int s[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn];
int n;
char str1[maxn];
int belong[maxn];
int le[4010];
int len;
int t;
int vis[4010];
int anspos;
int rk[maxn],height[maxn];
int lenn[maxn];
char str[4005][205];
int wa[maxn],wb[maxn],wsf[maxn],wv[maxn];

void build_sa(int m)
{
    int *x=t1,*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;i>=0;i--)sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1)
    {
       // cout<<1<<endl;
        int p=0;
        for(int i=n+1-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;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1,x[sa[0]]=0;
        for(int 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 cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void getsa(int *r,int *sa,int n,int m)
{
    int i,j,p,*x=wa,*y=wb,*t;
    for(i=0; i<m; i++)  wsf[i]=0;
    for(i=0; i<=n; i++)  wsf[x[i]=r[i]]++;
    for(i=1; i<m; i++)  wsf[i]+=wsf[i-1];
    for(i=n; i>=0; i--)  sa[--wsf[x[i]]]=i;
    p=1;
    j=1;
    for(; p<=n; j*=2,m=p)
    {
        for(p=0,i=n+1-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++)  wsf[i]=0;
        for(i=0; i<=n; i++)  wsf[wv[i]]++;
        for(i=1; i<m; i++)  wsf[i]+=wsf[i-1];
        for(i=n; i>=0; i--)  sa[--wsf[wv[i]]]=y[i];
        t=x;
        x=y;
        y=t;
        x[sa[0]]=0;
        for(p=1,i=1; i<=n; i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++;
    }
}
*/
void getheight()
{
    int k=0;
    for(int i=0;i<=n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])
        {
            k++;
        }
        height[rk[i]]=k;
    }
}
int check(int x,int n)
{
    for(int i=1;i<=n-1;i++)
    {
        if(height[i]<x) continue;
        int cnt=0;
        for(int j=0;j<=t;j++) vis[j]=0;
        while(height[i]>=x&&i<=n-1)
        {
            if(!vis[belong[sa[i-1]]])
            {
                vis[belong[sa[i-1]]]=1;
                cnt++;
            }
            i++;
        }
        if(!vis[belong[sa[i-1]]])
        {
            vis[belong[sa[i-1]]]=1;
            cnt++;
        }
        if(cnt>=t)
        {
            anspos=sa[i-1];
            return true;
        }
    }
    return false;
}
int main()
{
    int len;
    while(~scanf("%d",&t))
    {
        if(t==0) break;
        n=0;
        int pos=30;
        for(int i=0;i<t;i++)
        {
            scanf("%s",str[i]);
            lenn[i]=strlen(str[i]);
            for(int j=0;j<lenn[i];j++)
             {
                 s[n++]=str[i][j]-'a'+1;
                 belong[n-1]=i;
             }
            s[n++]=pos++;
        }
        s[n]=0;
        build_sa(5000);
        getheight();
        int l=1,r=200,mid;
        //for(int i=0;i<=n;i++)cout<<sa[i]<<endl;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid,n)) l=mid+1;
            else r=mid-1;
        }
        if(r==0) printf("IDENTITY LOST\n");
        else
        {
            //cout<<anspos<<endl;
            for(int i=anspos;i<anspos+r;i++)
                printf("%c",s[i]-1+'a');
            printf("\n");
        }
    }
    return 0;
}





第二题:

POJ3450

求多个串的最长公共子串,首先我们把所有字符串串在一起,求sa和height,然后我们对最长子串的答案进行二分,先预处理一下每个下标属于哪个字符串,如果有一段连续的height[i] 都大于等于mid 并且分别属于n个串,那么记录最后的anspos,从i=anspos到i<anspos+r 输出答案 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=807000;
int s[maxn];
int sa[maxn],t1[maxn],t2[maxn],c[maxn];
int n;
char str1[maxn];
int belong[maxn];
int le[4010];
int len;
int t;
int vis[4010];
int anspos;
int rk[maxn],height[maxn];
int lenn[maxn];
char str[4005][205];
int wa[maxn],wb[maxn],wsf[maxn],wv[maxn];
void build_sa(int m)
{
    int *x=t1,*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;i>=0;i--)sa[--c[x[i]]]=i;
    for(int k=1;k<=n;k<<=1)
    {
        int p=0;
        for(int i=n+1-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;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1,x[sa[0]]=0;
        for(int 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;
    }
}
void getheight()
{
    int k=0;
    for(int i=0;i<=n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])
        {
            k++;
        }
        height[rk[i]]=k;
    }
}
int check(int x,int n)
{
    for(int i=1;i<=n-1;i++)
    {
        if(height[i]<x) continue;
        int cnt=0;
        for(int j=0;j<=t;j++) vis[j]=0;
        while(height[i]>=x&&i<=n-1)
        {
            if(!vis[belong[sa[i-1]]])
            {
                vis[belong[sa[i-1]]]=1;
                cnt++;
            }
            i++;
        }
        if(!vis[belong[sa[i-1]]])
        {
            vis[belong[sa[i-1]]]=1;
            cnt++;
        }
        if(cnt>=t)
        {
            anspos=sa[i-1];
            return true;
        }
    }
    return false;
}
int main()
{
    int len;
    while(~scanf("%d",&t))
    {
        if(t==0) break;
        n=0;
        int pos=30;
        for(int i=0;i<t;i++)
        {
            scanf("%s",str[i]);
            lenn[i]=strlen(str[i]);
            for(int j=0;j<lenn[i];j++)
             {
                 s[n++]=str[i][j]-'a'+1;
                 belong[n-1]=i;
             }
            s[n++]=pos++;
        }
        s[n]=0;
        build_sa(5000);
        getheight();
        int l=1,r=200,mid;
        //for(int i=0;i<=n;i++)cout<<sa[i]<<endl;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid,n)) l=mid+1;
            else r=mid-1;
        }
        if(r==0) printf("IDENTITY LOST\n");
        else
        {
            //cout<<anspos<<endl;
            for(int i=anspos;i<anspos+r;i++)
                printf("%c",s[i]-1+'a');
            printf("\n");
        }
    }
    return 0;
}

第三题:

POJ3294

题意和上面的类似,求多个字符串的最长公共子串,并且这个公共子串满足属于大于n/2 的字符串的子串,并且有多个答案的时候全部输出,与第二题的做法类似,只是check的时候改成>n/2 返回值为true, 存答案的时候我们用anspos[++xans]去存,然后当满足条件的时候我们用xans更新ans,这样就能保证每次能覆盖掉原先的anspos而最后一个答案的ans却不会被覆盖

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=200050;
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn];
int belong[maxn],vis[maxn];
char str1[maxn];
int ans,anspos[maxn];
int xans;
int len;
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int m)
{
    int *x=t1,*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;i>=0;i--)sa[--c[x[i]]]=i;
    int p;
    for(int k=1;k<=n;k<<=1,m=p)
    {
        p=0;
        for(int i=n+1-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;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int i=1;i<=n;i++)
        {
            x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
        }
        if(p>n)break;
    }
}
void getheight()
{
    int k=0;
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rk[i]]=k;
    }
}

bool check(int x)
{
    xans=0;
    int flag=0;
    for(int i=1;i<=n-1;i++)
    {
        int cnt=0;
        if(height[i]<x)continue;
        for(int j=0;j<=num;j++)vis[j]=0;
        while(height[i]>=x&&i<=n-1)
        {
            if(!vis[belong[sa[i-1]]])
            {
                vis[belong[sa[i-1]]]=1;
                cnt++;
            }
            i++;
        }
        if(!vis[belong[sa[i-1]]])
        {
            vis[belong[sa[i-1]]]=1;
            cnt++;
        }
        if(cnt>num/2)
        {
            anspos[++xans]=sa[i-1];
            flag=1;
            cnt=0;
        }
    }
    if(flag)
    {
        ans=xans;
        return true;
    }
    return false;
}
int main()
{
    scanf("%d",&num);
    while(1)
    {
        ans=0;
        n=0;
        if(num==0)break;
        int pos=60;
        for(int i=1;i<=num;i++)
        {
            scanf("%s",str1);
            len=strlen(str1);
            for(int j=0;j<len;j++)
            {
                s[n++]=str1[j]-'a'+1;
                belong[n-1]=i;
            }
            s[n++]=pos++;
        }
        s[n]=0;
        build_sa(500);
        getheight();
        int l=1,r=1000,mid;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid))l=mid+1;
            else r=mid-1;
        }
        if(r==0)
        {
            printf("?\n");
        }
        else
        {
            for(int j=1;j<=ans;j++)
            {
                for(int i=anspos[j];i<anspos[j]+r;i++)
                {
                    printf("%c",s[i]+'a'-1);
                }
                printf("\n");
            }
        }
        scanf("%d",&num);
        if(num!=0)printf("\n");
        else break;
    }
}

第四题:

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

这题主要是利用sa数组和height数组的性质

1.固定一个子串i,对于和他排名相邻的串的lcp一定比和他排名相差更大的子串的lcp要大。

2.height数组的作用是把两个排名相邻的子串分在一起。

3.对于height数组逐渐递增的时候,排名后面的串与排名前面的串的lcp等于height数组的大小

例如如果前面是

1     2     3      4      5      1

BB BB   BAA    AB    BA

则对于3A来说 他和前面三个B的lcp 为height 数组的前缀和1+2+3;

对于 4A来说他和前面三个B的lcp也为height数组的前缀和1+2+3;

也就是说我们可以固定 当sa[i-1]为B时统计贡献sa[i]为A时累计答案。这样不会重复只会计算下划线的贡献

由于height数组只有递增的时候才有这个性质所以我们可以对height数组建立一个单调栈,当第六个BA为1

的时候我们把前面的2,3,4,5都出栈由于4,5没有产生贡献所以其数量统计num=0,我们一边出栈一边统计贡献的数量,并且不断更新前缀和pre, 

pre-=3, pre+=1;

pre-=2,pre+=1;

把2,3出栈后num累计为2,然后由于第6个height组合的sa[i-1]为B 会产生贡献所以入栈时,

cnt[top]=num+1;

pre+=1;

然后当sa[i]为A的时候我们累计答案,pre=1+1+1+1,  ans+=pre;

也就是说我们只要维护一个height数组的递增单调栈,一个贡献数量数组cnt ,一个贡献前缀和pre 就可以求出所有以sa[i]为A的贡献和 

但是 有可能为

BB BB BA AA AA AB  

这样我们会漏掉最后的B和前面的A 的lcp 所以我们对sa[i-1]=B sa[i]=A用单调栈求一遍lcp和,再对sa[i-1]=A,sa[i]=B再用单调栈求一遍lcp和 ,保证不重不漏, 复杂度为O(len(str1)+len(str2)) 

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=300050;
int n,len;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn];
int belong[maxn],vis[maxn];
char str1[maxn],str2[maxn];
int cnt[maxn];
int sta[maxn];
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int m)
{
    int *x=t1,*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;i>=0;i--)sa[--c[x[i]]]=i;
    int p;
    for(int k=1;k<=n;k<<=1,m=p)
    {
        p=0;
        for(int i=n+1-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;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int i=1;i<=n;i++)
        {
            x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
        }
        if(p>n)break;
    }
}
void getheight()
{
    int k=0;
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rk[i]]=k;
    }
}
int main()
{
    int k;
    while(scanf("%d",&k)==1)
    {
        if(k==0)break;
        n=0;
        scanf("%s",str1);
        len=strlen(str1);
        for(int i=0;i<len;i++)
        {
            s[n++]=str1[i]-'A'+1;
        }
        s[n++]=100;
        scanf("%s",str2);
        len=strlen(str2);
        for(int i=0;i<len;i++)
        {
            s[n++]=str2[i]-'A'+1;
        }
        s[n]=0;
        build_sa(300);
        getheight();
        len=strlen(str1);
        ll pre=0;
        ll ans=0;
        int top=0;
        for(int i=2;i<=n-1;i++)
        {
            if(height[i]<k)
            {
                top=0;
                pre=0;
            }
            else
            {
            int num=0;  //统计栈顶元素的数量
            while(top&&sta[top]>height[i])
            {
                pre-=1ll*cnt[top]*(sta[top]-k+1);
                pre+=1ll*cnt[top]*(height[i]-k+1);
                num+=cnt[top];
                top--;
            }
            sta[++top]=height[i];
            if(sa[i-1]>len)
            {
                pre+=1ll*(height[i]-k+1);
                cnt[top]=num+1;
            }
            else cnt[top]=num;
            if(sa[i]<len)
            {
                ans+=pre;
            }
            }
        }
        pre=0;
        top=0;
        for(int i=2;i<=n-1;i++)
        {
            if(height[i]<k)
            {
                top=0;
                pre=0;
            }
            else
            {
            int num=0;  //统计栈顶元素的数量
            while(top&&sta[top]>height[i])
            {
                pre-=1ll*cnt[top]*(sta[top]-k+1);
                pre+=1ll*cnt[top]*(height[i]-k+1);
                num+=cnt[top];
                top--;
            }
            sta[++top]=height[i];
            if(sa[i-1]<len)
            {
                pre+=1ll*(height[i]-k+1);
                cnt[top]=num+1;
            }
            else cnt[top]=num;
            if(sa[i]>len)
            {
                ans+=pre;
            }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

第五题:

URAL1297

求一个串的最长回文子串

把这个串的正向和逆向拼在一起,求sa和height以后,遍历height数组如果sa[i-1]和sa[i]分别属于两个子串,并且满足

sa[i-1]和sa[i]所代表的串在原字符串的同一位置则更新答案(两个串分别加上原字符串开头后长度相等)则更新答案,如果有长度相等的则保留最小下标。

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=200050;
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn];
int belong[maxn],vis[maxn];
char str1[maxn],str2[maxn];
int len;
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int m)
{
    int *x=t1,*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;i>=0;i--)sa[--c[x[i]]]=i;
    int p;
    for(int k=1;k<=n;k<<=1,m=p)
    {
        p=0;
        for(int i=n+1-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;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int i=1;i<=n;i++)
        {
            x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
        }
        if(p>n)break;
    }
}
void getheight()
{
    int k=0;
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rk[i]]=k;
    }
}
int main()
{

    while(~scanf("%s",str1))
    {
    len=strlen(str1);
    n=0;
    for(int i=0;i<len;i++)
    {
        s[n++]=str1[i]-'A'+1;
    }
    s[n++]=100;
    for(int i=len-1;i>=0;i--)
    {
        s[n++]=str1[i]-'A'+1;
    }
    s[n]=0;
    build_sa(300);
    getheight();
    int ans=1;
    int anspos=0;
    for(int i=2;i<=n-1;i++)
    {
        int mi=min(sa[i-1],sa[i]);
        int mx=max(sa[i-1],sa[i]);
        if(mi>len||mx<len)continue;
        if(mi+height[i]!=n-mx)continue;
        if(height[i]>ans)
        {
            ans=height[i];
            anspos=mi;
        }
        else if(height[i]==ans)
        {
            anspos=min(anspos,mi);
        }

    }
    for(int i=anspos,j=1;j<=ans;i++,j++)
    {
        printf("%c",s[i]+'A'-1);
    }
    printf("\n");
    }
    return 0;
}

第六题:

POJ3261

题意给你一个字符串,然后求一个出现大于等于k次并且最长的可重叠子串

长度我们可以二分答案,check的时候只要连续有k-1个height数组长度大于等于mid 就更新答案。

#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=2010150;
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn];
int belong[maxn],vis[maxn];
char str1[maxn];
int len;
int k;
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int m)
{
    int *x=t1,*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;i>=0;i--)sa[--c[x[i]]]=i;
    int p;
    for(int k=1;k<=n;k<<=1,m=p)
    {
        p=0;
        for(int i=n+1-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;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int i=1;i<=n;i++)
        {
            x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
        }
        if(p>n)break;
    }
}
void getheight()
{
    int k=0;
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])k++;
        height[rk[i]]=k;
    }
}
bool check(int x)
{
    int cnt=1;
    for(int i=2;i<=n;i++)
    {
        if(height[i]>=x)
        {
            cnt++;
        }
        else cnt=1;
        if(cnt==k)return true;
    }
    return false;
}
int main()
{
    while(scanf("%d%d",&num,&k)!=EOF)
    {
    int x;
    n=0;
    for(int i=1;i<=num;i++)
    {
        scanf("%d",&x);
        s[n++]=x;
    }
    s[n]=0;
    build_sa(1000110);
    getheight();
    int l,r,mid;
    l=0,r=n;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(check(mid))l=mid+1;
        else r=mid-1;
    }
    cout<<r<<endl;
    }
}

第七题

UVA - 12338 

给你n个字符串m个询问,每次询问两个字符串的lcp为多少

先把字符串拼接在一起然后用st表预处理出任意两个下标的lcp,预处理求出每个字符串的开头下标每次询问,O(1)查询两个字符串开头下标的lcp就行了。

貌似D3C并没有比倍增 快很多

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define ll long long
using namespace std;
const int maxn=1200000;  //字符总长度加上连接字符的个数
int n,num;
int s[maxn],t1[maxn],t2[maxn],rk[maxn],c[maxn],height[maxn],sa[maxn],dp[maxn][25];
char str1[maxn];
int lens[maxn],belong[maxn];
int len;
int cmp(int *r,int a,int b,int k)
{
    return r[a]==r[b]&&r[a+k]==r[b+k];
}
void build_sa(int n,int m)
{
    int *x=t1,*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;
    int p;
    for(int k=1;k<=n;k<<=1,m=p)
    {
        p=0;
        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];
        swap(x,y);
        p=1;
        x[sa[0]]=0;
        for(int i=1;i<n;i++)
        {
            x[sa[i]]=cmp(y,sa[i],sa[i-1],k)?p-1:p++;
        }
        if(p>=n)break;
    }
}
void getheight(int n)
{
    int k=0;
    for(int i=0;i<n;i++)rk[sa[i]]=i;
    for(int i=0;i<n;i++)
    {
        if(k)k--;
        int j=sa[rk[i]-1];
        while(s[i+k]==s[j+k])
        {
            k++;
        }
        height[rk[i]]=k;
    }
}

void st_build(int n)
{
    for(int i = 0; i < n; i++)
    {
        dp[i][0] = height[i];
    }
    for(int j = 1; (1<<j) <= n; j++)
        for(int i = 0; (i+(1<<j)-1) <= n; i++)
            dp[i][j] = min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
}

int query(int i, int j)
{
    int l = min(rk[i], rk[j]);
    int r = max(rk[i], rk[j]);
    ++l;
    int cnt = log2(r-l+1), len  = 1<<cnt;
    return min(dp[l][cnt], dp[r-len+1][cnt]);
}
int main()
{
    int t;
    scanf("%d",&t);
    int cont=0;
    while(t--)
    {
        scanf("%d",&num);
        n=0;
        int pos=50;
        for(int i=1;i<=num;i++)
        {
            scanf("%s",str1);
            len=strlen(str1);
            lens[i]=len;
            for(int j=0;j<len;j++)
            {
                s[n++]=str1[j]-'a'+1;
            }
            belong[i]=n-len;
            s[n++]=pos++;
        }
        s[n]=0;
        build_sa(n+1,100500);
        getheight(n+1);
        st_build(n+1);
        int q;
        int u,v;
        scanf("%d",&q);
        printf("Case %d:\n",++cont);
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&u,&v);
            if(u==v)printf("%d\n",lens[u]);
            else printf("%d\n",query(belong[u],belong[v]));
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值