字符串--后缀数组的运用

后缀数组的运用主要体现在三个数组的使用之中

1.sa[i]=n;
表示的是排名是第i名的是第n个后缀
2.rank[n]=i;
表示的是第n个后缀在排序中是排在第几位的,它和sa数组是相反的。
3.height[ ]
表示的是相邻的两个后缀之间的最长的公共前缀。
这三种数组的运用是有很多种,先总结两种。
一,求出现至少k次的最长不重复子串
这就是height[ ]最典型的运用。一般的思路是二分总的字符串的长度,来寻找符合条件的height[ ]中的段落,一旦出现符合条件的段落就记录下来,同时输出就可以。
例题  UVA - 11107
题意:给出n个字符串,要求你找出至少在(n)/2个字符串中出现过的最长字串。
思路;这是一道典型的使用height[ ]数组的题目,这里只需要将给出的字符串都合并起来,同时在每一个合并的地方加上一个从来都没有出现过的字符,避免在两个字符串相交的地方出现符合条件的字串。同时在最后合并的最后加上一个没有出现过的最小的字符。这是为了,在建立三个数组的时候更好操作。所以这题我们只要对一个长度进行二分,然后在height[ ]数组中分段就好了。那么我们选择什么长度呢,这里只要选择所有给出的串中最长的一个,显然这是很符合条件的。那么下面就是代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int  mod =1000000;
const int maxn =140110;
char  s[1100];
int str[maxn];
int Rank[maxn],height[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int belong[maxn],visit[200];
void build_sa(int * s,int n,int m)
{
	int i,*x = t,*y = t2;
	for(i = 0;i < m;i++)c[i] = 0;
	for(i = 0;i < n;i++)c[ x[i] = s[i] ]++;
	for(i = 1;i < m;i++)c[i] += c[i-1];
	for(i = n-1;i >= 0;i--)	sa[--c[x[i]]] = i;
	for(int k = 1;k <= n;k <<= 1)
	{
		int p = 0;
		for(i = n - k;i < n;i++)	y[p++] = i;
		for(i = 0;i < n;i++)	if(sa[i] >= k)	y[p++] = sa[i] - k;
		for(i = 0;i < m;i++)	c[i] = 0;
		for(i = 0;i < n;i++)	c[ x[y[i]] ]++;
		for(i = 0;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);
		p = 1;	x[sa[0]] = 0;
		for(i = 1;i < n;i++)
			x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p-1:p++;
		if(p >= n)	break;
		m = p;
	}
}

void calheight(int * s,int n)
{
	int i,j,k = 0;
	for(i = 0;i < n;i++)Rank[sa[i]] = i;
	for(i = 0;i < n;i++)
	{
		if(k) k--;
		int j = sa[Rank[i]-1];
		while(s[i+k] == s[j+k])	k++;
		height[Rank[i]] = k;
	}
}
int Judge(int n,int len,int num)
{
    int i,j,k;
    int cnt=0;
    memset(visit,0,sizeof(visit));
    visit[0]=1;
    if(!visit[belong[sa[0]]])
    {
        cnt++;
        visit[belong[sa[0]]]=1;
    }
    for(i=1;i<n;i++)
    {
        if(height[i]<len)
        {
            cnt=0;
            memset(visit,0,sizeof(visit));
            visit[0]=1;
            if(!visit[belong[sa[i]]])
            {
                cnt++;
                visit[belong[sa[i]]]=1;
            }
        }
        else
        {
            if(!visit[belong[sa[i]]])
            {
                cnt++;
                visit[belong[sa[i]]]=1;
            }
        }
        if(cnt>=num) return 1;
    }
    return 0;
}
void out(int n,int len,int num)
{
    int i,j,cnt=0;
    memset(visit,0,sizeof(visit));
    visit[0]=1;
    if(!visit[belong[sa[0]]])
    {
        cnt++;
    }
    visit[belong[sa[0]]]=1;
    for(i=1;i<n;i++)
    {
        if(height[i]<len)
        {
            if(cnt>=num)
            {
                for(j=sa[i-1];j<sa[i-1]+len;j++)
                    printf("%c",str[j]-20);
                printf("\n");
            }
            cnt=0;
            memset(visit,0,sizeof(visit));
            visit[0]=1;
            if(!visit[belong[sa[i]]])
            {
                cnt++;
                visit[belong[sa[i]]]=1;
            }
        }
        else
        {
            if(!visit[belong[sa[i]]])
            {
                cnt++;
                visit[belong[sa[i]]]=1;
            }
        }
    }
    if(cnt>=num)
    {
        for(j=sa[n-1];j<sa[n-1]+len;j++)
            printf("%c",str[j]-20);
        printf("\n");
    }
}
int main()
{
    int n;
    int flag=1;
    while(scanf("%d",&n)==1&&n)
    {
        if(!flag) printf("\n");
        else flag=0;
        int i,j,k;
        int pos=0,cnt=1,left=0,right=0;
        memset(belong,0,sizeof(belong));

        for(i=1;i<=n;i++)
        {
            scanf("%s",s);
            int num=strlen(s);
            right=max(right,num);
            for(j=0;j<num;j++)
            {
                str[pos+j]=int(s[j])+20;
                belong[pos+j]=i;
            }
            str[pos+num]=cnt++;
            pos=pos+num+1;
        }
        str[pos]=0;
        build_sa(str,pos+1,150);
        calheight(str,pos+1);
        int max_x=0;
        while(left<=right)
        {
            int mid=(left+right)>>1;
            if(Judge(pos+1,mid,n/2+1))
            {
                max_x=mid;
                left=mid+1;
            }
            else
            {
                right=mid-1;
            }
        }
        //cout<<max_x<<"....."<<endl;
        if(max_x==0)
        {
            printf("?\n");
        }
        else
        {
            out(pos+1,max_x,n/2+1);
        }
    }
    return 0;
}
2.求出现k次或者是至少k次的最长可重叠子串
这个问题和上面的问题就很类似,实际上差别就不会很大,上面的要求是不能重叠,而这里则是可以重叠的,那么反而这个类型是更加简单的。同样的方法解答,但是不需要记录每一个对height[ ]遍历的时候辨别当前的子串是不是属于同一个已经辨别过的子串。
题意:给你一个n和一个字符串,要求你求出在这个字符串中出现了n次的最长字串的长度以及最后一个出现的位置。
思路;这一题时间上十分的简单,只要二分height[ ]数组,同时注意输出就好了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int  mod =1000000;
const int maxn =140110;
char  s[50000];
int str[maxn];
int Rank[maxn],height[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int max_i;
int max_x=0;
void build_sa(int * s,int n,int m)
{
	int i,*x = t,*y = t2;
	for(i = 0;i < m;i++)c[i] = 0;
	for(i = 0;i < n;i++)c[ x[i] = s[i] ]++;
	for(i = 1;i < m;i++)c[i] += c[i-1];
	for(i = n-1;i >= 0;i--)	sa[--c[x[i]]] = i;
	for(int k = 1;k <= n;k <<= 1)
	{
		int p = 0;
		for(i = n - k;i < n;i++)	y[p++] = i;
		for(i = 0;i < n;i++)	if(sa[i] >= k)	y[p++] = sa[i] - k;
		for(i = 0;i < m;i++)	c[i] = 0;
		for(i = 0;i < n;i++)	c[ x[y[i]] ]++;
		for(i = 0;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);
		p = 1;	x[sa[0]] = 0;
		for(i = 1;i < n;i++)
			x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p-1:p++;
		if(p >= n)	break;
		m = p;
	}
}

void calheight(int * s,int n)
{
	int i,j,k = 0;
	for(i = 0;i < n;i++)Rank[sa[i]] = i;
	for(i = 0;i < n;i++)
	{
		if(k) k--;
		int j = sa[Rank[i]-1];
		while(s[i+k] == s[j+k])	k++;
		height[Rank[i]] = k;
	}
}
int Judge(int n,int len,int num)
{
    int cnt=1;
    int i,j;
    for(i=1;i<n;i++)
    {
        if(height[i]<len)
        {
            cnt=1;
        }
        else
        {
            cnt++;
        }
        if(cnt>=num)
        {
            return 1;
        }
    }
    return 0;
}
void out(int n,int len,int num)
{
    int cnt=1;
    int i,j;
    for(i=1;i<n;i++)
    {
        if(height[i]<len)
        {
            cnt=1;
        }
        else
        {
            cnt++;
        }
        if(cnt>=num)
        {
            int num2=cnt;
            for(j=i-cnt+1;j<=i;j++)
            {
                if(sa[j]>=max_i)
                {
                    max_i=sa[j];
                }
            }
        }
    }
    printf("%d %d\n",max_x,max_i);
}
int main()
{
    int n;
    while(scanf("%d",&n)==1&&n)
    {
        int i,j,k;
        scanf("%s",s);
        int num=strlen(s);
        for(i=0;i<num;i++)
        {
            str[i]=int(s[i]);
        }
        str[num]=0;
        build_sa(str,num+1,200);
        calheight(str,num+1);
        int l=0,r=num;
        max_x=0;
        max_i=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(Judge(num+1,mid,n))
            {
                max_x=mid;
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
        }
        if(n==1)
        {
            printf("%d 0\n",num);
        }
        else{
        if(max_x==0)
        {
            printf("none\n");
        }
        else
        {
            out(num+1,max_x,n);
            //cout<<max_x<<endl;
        }
        }
    }
    return 0;
}

  待续....

  

posted @ 2017-11-07 21:48  静静的逻辑  阅读(576)  评论(0编辑  收藏  举报