后缀数组练习题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/zyszlb2003/article/details/99932720

POJ1743

思路

由于可能转调,

现设匹配长度 l e n len A [ i i + l e n 1 ] = A [ j j + l e n 1 ] w ( j > i + l e n 1 ) A[i\sim i+len-1]=A[j\sim j+len-1]-w(j>i+len-1) ,w就是转调值,但这样并不好处理。

由于是区间加上(减去)一个值,可以考虑差分,差分之后,得到的两两之差,是一定的。

现假定是 i i i + 1 i+1 的差,原数组匹配转为差分数组也就是: B [ i i + l e n 2 ] = B [ j j + l e n 2 ] ( j > i + l e n 1 ) B[i\sim i+len-2]=B[j\sim j+len-2](j>i+len-1)

回忆一下, h e i g h t height 数组的定义,不难想到就是当 h e i g h t [ i ] k height[i]\ge k ,可以拓展状态。

那么问题就迎刃而解了,转为二分+后缀数组。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=2e4+10;
int s[N],wa[N],wb[N],c[N],wv[N],n,sa[N];
inline bool cmp(int *s,int a,int b,int l){return s[a]==s[b]&&s[a+l]==s[b+l];}
void DA(int *s)
{
    int m=188,*x=wa,*y=wb,*t;
    for(int i=1;i<=n;i++)++c[x[i]=s[i]];
    for(int i=2;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
    for(int j=1,p=0;p<n;j<<=1,m=p)
    {
        p=0;
        for(int i=n-j+1;i<=n;i++)y[++p]=i;
        for(int i=1;i<=n;i++)if(sa[i]>j)y[++p]=sa[i]-j;
        for(int i=1;i<=n;i++)wv[i]=x[y[i]];
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)++c[wv[i]];
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[wv[i]]--]=y[i];
        t=x;x=y;y=t;p=1;x[sa[1]]=1;
        for(int i=2;i<=n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p:++p;
    }
}
int rk[N],height[N];
void calheight(int *s,int *sa)
{
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=1,k=0,j;i<=n;height[rk[i++]]=k)//k=h[i-1]
        for(k?k--:0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);//for(k=h[i-1]-1>=0?h[i-1]-1:0)
}
bool check(int k)
{
    int l,r;l=r=sa[1];  
    for(int i=2;i<=n;i++)
    {
        if(height[i]<k)l=r=sa[i];
        else
        {
            if(sa[i]>r)r=sa[i];
            if(sa[i]<l)l=sa[i];
            if(r-l>k)return true;
        }
    }
    return false;
}
void solve()
{
    int l=0,r=n>>1;
    while(l<r)
    {
        int mid=(l+r+1)>>1;
        if(check(mid))l=mid;
        else r=mid-1;
    }
    if(l<4)puts("0");
    else printf("%d\n",l+1);
}
int main()
{
    //freopen("a0.in","r",stdin);
    while(scanf("%d",&n)&&n)
    {
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)scanf("%d",&s[i]);--n;
        for(int i=1;i<=n;i++)s[i]=s[i+1]-s[i]+100;
        DA(s);calheight(s,sa),solve();
    }
    return 0;
}

BZOJ1717

由于是权限题,暂不提供链接。

思路

和上一题差不多。

可以用单调队列维护,也可以二分求解。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=2e4+10;
int a[N],b[N],wa[N],wb[N],wv[N],c[N],n,m,k,sa[N];
void disc()
{
    sort(a+1,a+n+1);m=0;
    for(int i=1;i<=n;i++)
        if(i==1||a[i]!=a[i-1])a[++m]=a[i];
}
inline int get(int d)
{
    int l=1,r=m;
    while(l<r)
    {
        int mid=l+r>>1;
        if(a[mid]<d)l=mid+1;
        else r=mid;
    }
    return l;
}
inline bool cmp(int *s,int a,int b,int l){return s[a]==s[b]&&s[a+l]==s[b+l];}
void DA(int *s)
{
    int *x=wa,*y=wb,*t;
    for(int i=1;i<=n;i++)++c[x[i]=s[i]];
    for(int i=2;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
    for(int k=1,p=0;p<n;k<<=1,m=p)
    {
        p=0;
        for(int i=n-k+1;i<=n;i++)y[++p]=i;
        for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k;
        for(int i=1;i<=n;i++)wv[i]=x[y[i]];
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)++c[wv[i]];
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[wv[i]]--]=y[i];
        t=x;x=y;y=t;p=1;x[sa[1]]=1;
        for(int i=2;i<=n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p:++p;
    }
}
int rk[N],height[N];
void calheight(int *s,int *sa)
{
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=1,k=0,j;i<=n;height[rk[i++]]=k)
        for(k?k--:k=0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);//sa[
}
bool check(int len)
{
    int ans=1;
    for(int i=2;i<=n;i++)
    {
        if(height[i]<len)ans=1;
        else {++ans;if(ans==k)return true;}
    }
    return false;
}
int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)scanf("%d",&b[i]),a[i]=b[i];
        disc();for(int i=1;i<=n;i++)b[i]=get(b[i]);
        DA(b);calheight(b,sa);
        int l=0,r=m;
        while(l<r)
        {
            int mid=l+r+1>>1;
            if(check(mid))l=mid;
            else r=mid-1;
        }
        printf("%d\n",l);
    }
    return 0;
}

caioj1469

思路

把两个字串拼在一起之后,求 h e i g h t height 数组。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std;
const int N=2e5+10;
char s[N];int wa[N],wb[N],wv[N],sa[N],c[30],rk[N],height[N],n;
inline bool cmp(int *s,int a,int b,int l){return s[a]==s[b]&&s[a+l]==s[b+l];}
void DA(char *s)
{
    int m=28;
    int *x=wa,*y=wb,*t;
    for(int i=1;i<=n;i++)++c[x[i]=s[i]-'a'+1];
    for(int i=2;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
    for(int k=1,p;k<n;k<<=1,m=p)
    {
        p=0;
        for(int i=n-k+1;i<=n;i++)y[++p]=i;
        for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k;
        for(int i=1;i<=n;i++)wv[i]=x[y[i]];
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)++c[wv[i]];
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[wv[i]]--]=y[i];
        t=x;x=y;y=t;p=1;x[sa[1]]=1;
        for(int i=2;i<=n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p:++p;
    }
}
void calheight(char *s,int *sa)
{
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=1,j,k=0;i<=n;height[rk[i++]]=k)
        for(k?k--:k=0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
}
int main()
{
    scanf("%s",s+1);n=strlen(s+1);
    ++n;s[n]='a'+27;int len=n;
    scanf("%s",s+n+1);n=strlen(s+1);int ans=0;
    DA(s);calheight(s,sa);
    for(int i=2;i<=n;i++)if((sa[i-1]<len&&sa[i]>len)||(sa[i-1]>len&&sa[i]<len))ans=max(ans,height[i]);
    printf("%d\n",ans);
    return 0;
}

caioj1470

思路

和上一题差不了多少,把字串拼起来之后,二分判断 h e i g h t height 数组即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int N=1e5+10;
int wa[N],wb[N],wv[N],c[N],sa[N],a[N],rk[N],height[N],n;
inline bool cmp(int *s,int a,int b,int l){return s[a]==s[b]&&s[a+l]==s[b+l];}
void DA(int *s)
{
    int *x=wa,*y=wb,*t,m=310;
    for(int i=1;i<=n;i++)++c[x[i]=s[i]];
    for(int i=2;i<=m;i++)c[i]+=c[i-1];
    for(int i=n;i>=1;i--)sa[c[x[i]]--]=i;
    for(int k=1,p;p<n;k<<=1,m=p)
    {
        p=0;
        for(int i=n-k+1;i<=n;i++)y[++p]=i;//第二关键字排序 
        for(int i=1;i<=n;i++)if(sa[i]>k)y[++p]=sa[i]-k;//y[i]表示排名为i的第二关键字属于哪个后缀
        for(int i=1;i<=n;i++)wv[i]=x[y[i]];//第一关键字排序。
        for(int i=1;i<=m;i++)c[i]=0;
        for(int i=1;i<=n;i++)++c[wv[i]];
        for(int i=2;i<=m;i++)c[i]+=c[i-1];
        for(int i=n;i>=1;i--)sa[c[wv[i]]--]=y[i];
        t=x;x=y;y=t;p=1;x[sa[1]]=1;
        for(int i=2;i<=n;i++)
            x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p:++p;  
    }
}
int len[105],id[N],cnt,ans[1005],t,ansl;bool v[105];
void calheight(int *s,int *sa)
{
    for(int i=1;i<=n;i++)rk[sa[i]]=i;
    for(int i=1,k=0,j;i<=n;height[rk[i++]]=k)
        for(k?k--:k=0,j=sa[rk[i]-1];s[i+k]==s[j+k];k++);
    for(int i=1,j=1;i<=cnt;i++)
    {
        for(int k=j;k<=j+len[i]-1;k++)id[rk[k]]=i;
        j+=len[i]+1;
    }
}
bool check(int x)
{
    memset(v,1,sizeof(v));
    int tot=0,size=0;bool bk=0;
    for(int i=1;i<=n;i++)
    {
        if(height[i]<x)
        {
            if(tot>(t>>1))ans[++size]=sa[i-1],bk=1;
            memset(v,1,sizeof(v));tot=0;
        }
        else
        {
            if(v[id[i]])v[id[i]]=0,++tot;
            if(v[id[i-1]])v[id[i-1]]=0,++tot;
        }
    }
    if(tot>(t>>1))ans[++size]=sa[n],bk=1;
    if(size)ans[0]=size,ansl=x;
    return bk;
}
char s[1005];
int main()
{
    bool bk=1;
    while(scanf("%d",&t)&&t)
    {
        n=0;cnt=t;memset(c,0,sizeof(c));
        bk?bk=0:puts("");
        if(t==1){scanf("%s",s+1);printf("%s\n",s+1);continue;}
        for(int i=1;i<=t;i++)
        {
            scanf("%s",s+1);len[i]=strlen(s+1);
            for(int j=1;j<=len[i];j++)a[++n]=s[j];
            a[++n]='z'+i;
        }
        DA(a);
        calheight(a,sa);
        int l=0,r=n;ans[0]=ansl=0;
        while(l<r)
        {
            int mid=l+r+1>>1;
            if(check(mid))l=mid;
            else r=mid-1;
        }
        if(!ans[0]){puts("?");continue;}
        for(int i=1;i<=ans[0];i++)
        {
            for(int j=ans[i];j<ans[i]+ansl;j++)printf("%c",a[j]);
            puts("");
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zyszlb2003/article/details/99932720