后缀数组

P3809 【模板】后缀排序

题意:给定字符串,输出后缀数组就可以了

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstring>
#include <string>
using namespace std;
const int maxn=1e6+5;

struct suffixvec
{
	int c[maxn],sa[maxn],rank[maxn],height[maxn],top[maxn];
	int m,len;
	char s[maxn];
	void init(int len1,char *s1)
	{
		len=len1;
		strcpy(s+1,s1+1);

	}	
	void clear()
	{
		for(int i=1;i<=len;++i)
			sa[i]=rank[i]=height[i]=0;		
	}
	void qsort()
	{
		for(int i=0;i<=m;++i)
			c[i]=0;
		for(int i=1;i<=len;++i)
			c[rank[i]]++;
		for(int i=1;i<=m;++i)
			c[i]+=c[i-1];
		for(int i=len;i>=1;--i)
			sa[c[rank[top[i]]]--]=top[i];
	}
	void get_sa()
	{
		m=200;
		for(int i=1;i<=len;++i)
		{
			rank[i]=s[i];
			top[i]=i;
		}
		qsort();
		for(int k=1,p=0;p<len;m=p,k<<=1)
		{
			p=0;
			for(int i=1;i<=k;++i)
				top[++p]=len-k+i;
			for(int i=1;i<=len;++i)
				if(sa[i]>k)
					top[++p]=sa[i]-k;
			qsort();
			swap(top,rank);
			rank[sa[1]]=p=1;
			for(int i=2;i<=len;++i)
				rank[sa[i]]=(top[sa[i-1]]==top[sa[i]]&&top[sa[i-1]+k]==top[sa[i]+k])?p:++p;
		}	
	}
	int solve(int x,int y)
	{
		int ret=0;
		while(s[x++]==s[y++])
			ret++;
		return ret;
	}
	void get_height()
	{
		int cur=0;
		for(int i=1;i<=len;++i)
		{
			if(cur!=0)
				cur--;
			height[rank[i]]=cur=cur+solve(i+cur,sa[rank[i]+1]+cur);
		}
	}
}suffix;

int main()
{
	char s[maxn];
	scanf("%s",s+1);
	int n=strlen(s+1);
	suffix.init(n,s);
	suffix.clear();
	suffix.get_sa();
	for(int i=1;i<=n;++i)
		printf("%d%c",suffix.sa[i],i==n?'\n':' ');  
	return 0;
}

2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛 K-th occurrence HDU - 6704

题意:给定一个字符串,给定一个查询(l,r,k)求,s[l,r]这个子串第k次出现的首位置
思路后缀数组+ST表+二分+主席树
每一个子串都是后缀的前缀,然后在后缀数组中,按字典序排序后,前缀相同的都是靠在一起的。假设这个子串的长度为len,那么和rank[l]的lcp大于等于len的,都是含有这个子串的。然后二分出上下区间就可以了。(一个固定的串与其他串的距离越远lcp越小,具有单调性)
找到了含有这个子串的区间后,我们要找第k个出现的首位置,也就是sa数组的第k小,对sa数组建立主席树,求第k小就可以了

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;


struct RMQ
{
    int dp[maxn][30];
    void init(int len,int *a)
    {
        for(int i=1;i<=len;++i)
            dp[i][0]=a[i];
        for(int j=1;(1<<j)<=len;++j)
            for(int i=1;i+(1<<j)-1<=len;++i)
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
    }    
    int QueryMin(int l,int r)
    {
        int k=log2(r-l+1);
        return min(dp[l][k],dp[r-(1<<k)+1][k]);
    }
}rmq;

struct suffixvec
{
    int c[maxn],sa[maxn],rank[maxn],height[maxn],x[maxn],y[maxn];
    int m,n;
    char s[maxn];
    void init(int len1,char *s1)
    {
        n=len1;
        strcpy(s+1,s1+1);

    }    
    void clear()
    {
        for(int i=1;i<=n;++i)
            sa[i]=rank[i]=height[i]=0;        
    }
    void qsort()
    {
        for(int i=0;i<=m;++i)
            c[i]=0;
        for(int i=1;i<=n;++i)
            c[rank[i]]++;
        for(int i=1;i<=m;++i)
            c[i]+=c[i-1];
        for(int i=n;i>=1;--i)
            sa[c[rank[y[i]]]--]=y[i];
    }
    
    void get_sa()
    {
        m=128;
        for(int i=1;i<=n;++i)
        {
            rank[i]=s[i];
            y[i]=i;
        }
        qsort();
        for(int k=1;k<=n;k<<=1)
        {
            int p=0;
            for(int i=1;i<=k;++i)
                y[++p]=n-k+i;
            for(int i=1;i<=n;++i)
                if(sa[i]>k)
                    y[++p]=sa[i]-k;
            qsort();
            swap(y,rank);
            rank[sa[1]]=p=1;
            for(int i=2;i<=n;++i)
                rank[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k])?p:++p;
            if(p>=n)
                break;
            m=p;
        }    
    }

    void get_height()
    {
        for(int i=1;i<=n;++i)
            rank[sa[i]]=i;
        int k=0;
        for(int i=1;i<=n;++i)
        {
            if(k)
                k--;
            int j=sa[rank[i]-1];
            while(s[j+k]==s[i+k])
                k++;
            height[rank[i]]=k;
        }
        return;
    }
    int lcp(int x,int y)
    {
        int l=rank[x],r=rank[y];
        if(l>r)
            swap(l,r);
        if(l==r)
            return n-x+1;
        return rmq.QueryMin(l+1,r);
    }
}suffix;

int t,n,q;
char s[maxn];


int version[maxn],ST[maxn*40],ls[maxn*40],rs[maxn*40],no; 

int Build(int L,int R)
{
    int rt=++no;
    if(L==R)
        return rt;
    int mid=(L+R)>>1;
    Build(L,mid);
    Build(mid+1,R);
    return rt;
}

int Update(int pre,int p,int L,int R)
{
    int rt=++no;
    ls[rt]=ls[pre];
    rs[rt]=rs[pre];
    ST[rt]=ST[pre]+1;
    if(L==R)
        return rt;
    int mid=(L+R)>>1;
    if(p<=mid)
        ls[rt]=Update(ls[pre],p,L,mid);
    if(p>mid)
        rs[rt]=Update(rs[pre],p,mid+1,R);
    return rt; 
} 

int Query(int pre,int now,int k,int L,int R)
{
    if(L==R)
        return L;
    int mid=(L+R)>>1;
    int x=ST[ls[now]]-ST[ls[pre]];
    if(k<=x)
        return Query(ls[pre],ls[now],k,L,mid);
    else
        return Query(rs[pre],rs[now],k-x,mid+1,R);
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&q);
        scanf("%s",s+1);
        
        suffix.init(n,s);
        suffix.clear();        
        suffix.get_sa();
        suffix.get_height();
        
        rmq.init(n,suffix.height);
        
        no=0;
        version[0]=Build(1,n);
        for(int i=1;i<=n;++i)
            version[i]=Update(version[i-1],suffix.sa[i],1,n);
    
        for(int i=1;i<=q;++i)
        {
            int l,r,k;
            scanf("%d%d%d",&l,&r,&k);
            
            int L=1,R=suffix.rank[l],length=r-l+1;
            int ansl=suffix.rank[l],ansr=suffix.rank[l];
            while(L<R)
            {
                int mid=(L+R)>>1;
                if(suffix.lcp(suffix.sa[mid],l)>=length)
                    R=mid;
                else
                    L=mid+1;            
            }
            ansl=L;
            

            L=suffix.rank[l],R=n;
            while(L<R)
            {
                int mid=(L+R+1)>>1;
                if(suffix.lcp(suffix.sa[mid],l)>=length)
                    L=mid;
                else
                    R=mid-1;
            }
            ansr=L;
            
            
            //printf("l=%d r=%d\n",ansl,ansr); 
            
            if(ansr-ansl+1<k)
                printf("-1\n");
            else
                printf("%d\n",Query(version[ansl-1],version[ansr],k,1,n));    
        }        
    }
    return 0;
}

Boring counting HDU - 3518

题意:求字符串中子串出现两次的以上,且不重叠的子串个数
思路:枚举长度,遍历height数组,如果lcp的长度大于枚举长度,说明这个子串是存在的。当不满足了,那就可以枚举长度相同的下一种子串了。每一种子串在枚举的过程中,记录好该种子串的最左和最右端点。满足r-l>=len的。则答案+1

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=2010,INF=0x3f3f3f3f;
const int mod=1e9+7;

struct RMQ
{
    int dp[maxn][30];
    void init(int len,int *a)
    {
        for(int i=1;i<=len;++i)
            dp[i][0]=a[i];
        for(int j=1;(1<<j)<=len;++j)
            for(int i=1;i+(1<<j)-1<=len;++i)
                dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
    }    
    int QueryMin(int l,int r)
    {
        int k=log2(r-l+1);
        return min(dp[l][k],dp[r-(1<<k)+1][k]);
    }
}rmq;

struct suffixvec
{
    int c[maxn],sa[maxn],rank[maxn],height[maxn],x[maxn],y[maxn];
    int m,n;
    char s[maxn];
    void init(int len1,char *s1)
    {
        n=len1;
        strcpy(s+1,s1+1);

    }    
    void clear()
    {
        for(int i=1;i<=n;++i)
            sa[i]=rank[i]=height[i]=0;        
    }

    void get_sa()
    {
        m=128;
        for(int i=0;i<=m;++i)
            c[i]=0;
        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;k<=n;k<=1)
        {
            for(int i=0;i<=m;++i)
                y[i]=0;
            
            int 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=0;i<=m;++i)
                c[i]=0;
            for(int i=1;i<=n;++i)
                c[x[y[i]]]++;
                
                            
            for(int i=1;i<=m;++i)
                c[i]+=c[i-1];
                
            for(int i=n;i>=1;--i)
                sa[c[x[y[i]]]--]=y[i];
            swap(x,y);
            x[sa[1]]=1;
            p=1;
            for(int i=2;i<=n;++i)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
            if(p==n)
                break;
            m=p;
        }
    }

    void get_height()
    {
        for(int i=1;i<=n;++i)
            rank[sa[i]]=i;
        int k=0;
        for(int i=1;i<=n;++i)
        {
            if(k)
                k--;
            int j=sa[rank[i]-1];
            while(s[j+k]==s[i+k])
                k++;
            height[rank[i]]=k;
        }
        return;
    }
    int lcp(int x,int y)
    {
        int l=rank[x],r=rank[y];
        if(l>r)
            swap(l,r);
        if(l==r)
            return n-x+1;
        return rmq.QueryMin(l+1,r);
    }
}suffix;

char ss[maxn];

int main()
{
    while(scanf("%s",ss+1)&&ss[1]!='#')
    {
        int n=strlen(ss+1);
        suffix.init(n,ss);
        suffix.clear();
        suffix.get_sa();
        suffix.get_height();
        
        int ans=0;
        for(int len=1;len<=n/2;++len)
        {
            int maxx=suffix.sa[1],minn=suffix.sa[1];
            for(int i=2;i<=n;++i)
            {
                if(suffix.height[i]>=len)
                {
                    maxx=max(maxx,suffix.sa[i]);
                    minn=min(minn,suffix.sa[i]);
                }
                else
                {
                    if(maxx-minn>=len)
                        ans++;
                    maxx=suffix.sa[i];
                    minn=suffix.sa[i];
                }        
            }
            if(maxx-minn>=len)
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

1、一个串中两个串的最大公共前缀是多少?
这不就是Height吗?用rmq预处理,再O(1)查询。

2、一个串中可重叠的重复最长子串是多长?
就是求任意两个后缀的最长公共前缀,而任意两个后缀的最长公共前缀都是Height 数组里某一段的最小值,那最长的就是Height中的最大值。

3、一个串种不可重叠的重复最长子串是多长?
先二分答案,转化成判别式的问题比较好处理。假设当前需要判别长度为k是否符合要求,只需把排序后的后缀分成若干组,其中每组的后缀之间的Height 值都不小于k,再判断其中有没有不重复的后缀,具体就是看最大的SA值和最小的SA值相差超不超过k,有一组超过的话k就是合法答案。

4、一个字符串不相等的子串的个数是多少?
每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。而且可以发现每一个后缀Suffix[SA[i]]的贡献是Len - SA[i] + 1,但是有子串算重复,重复的就是Heigh[i]个与前面相同的前缀,那么减去就可以了。最后,一个后缀Suffix[SA[i]]的贡献就是Len - SA[k] + 1 - Height[k]。
原文链接:https://blog.csdn.net/YxuanwKeith/article/details/50636898

随便贴几个板子
1、这个板子可以过Boring counting HDU - 3518,却过不了K-th occurrence HDU - 6704,不明白

struct suffixvec
{
    int c[maxn],sa[maxn],rank[maxn],height[maxn],x[maxn],y[maxn];
    int m,n;
    char s[maxn];
    void init(int len1,char *s1)
    {
        n=len1;
        strcpy(s+1,s1+1);

    }    
    void clear()
    {
        for(int i=1;i<=n;++i)
            sa[i]=rank[i]=height[i]=0;        
    }

    void get_sa()
    {
        m=128;
        for(int i=0;i<=m;++i)
            c[i]=0;
        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;k<=n;k<=1)
        {
        	for(int i=0;i<=m;++i)
        		y[i]=0;
        	
            int 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=0;i<=m;++i)
                c[i]=0;
            for(int i=1;i<=n;++i)
                c[x[y[i]]]++;
                
                            
            for(int i=1;i<=m;++i)
                c[i]+=c[i-1];
                
            for(int i=n;i>=1;--i)
                sa[c[x[y[i]]]--]=y[i];
            swap(x,y);
            x[sa[1]]=1;
			p=1;
            for(int i=2;i<=n;++i)
                x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
            if(p==n)
                break;
            m=p;
        }
    }

    void get_height()
    {
        for(int i=1;i<=n;++i)
            rank[sa[i]]=i;
        int k=0;
        for(int i=1;i<=n;++i)
        {
            if(k)
                k--;
            int j=sa[rank[i]-1];
            while(s[j+k]==s[i+k])
                k++;
            height[rank[i]]=k;
        }
        return;
    }
    int lcp(int x,int y)
    {
        int l=rank[x],r=rank[y];
        if(l>r)
            swap(l,r);
        if(l==r)
            return n-x+1;
        return rmq.QueryMin(l+1,r);
    }
}suffix;

参考博客:http://www.mamicode.com/info-detail-2759502.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值