码迷,mamicode.com
首页 > 编程语言 > 详细

【后缀数组】【二分答案】poj3261

时间:2015-04-28 11:02:58      阅读:181      评论:0      收藏:0      [点我收藏+]

标签:

注意:对整型数组求sa时,s[n]请置成-1。

请离散化。

可重叠的 k 次最长重复子串(pku3261)
给定一个字符串,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠。
算法分析:
先二分答案,然后将后缀分成若干组。 不
同的是,这里要判断的是有没有一个组的后缀个数不小于 k。如果有,那么存在
k 个相同的子串满足条件,否则不存在。这个做法的时间复杂度为 O(nlogn)。

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 20001
struct Point{int p,v;}T[N];
bool operator < (Point a,Point b){return a.v<b.v;}
int s[N],tong[N],t[N],t2[N],rank[N],lcp[N],sa[N];
int n,K,zy=1,ma[N];
bool cmp(int *y,int i,int k)
{
	return ((y[sa[i-1]]==y[sa[i]])&&((sa[i-1]+k>=n?-1:y[sa[i-1]+k])==(sa[i]+k>=n?-1:y[sa[i]+k])));
}
void build_sa(int range)
{
	int *x=t,*y=t2;
	memset(tong,0,sizeof(int)*range);
	for(int i=0;i<n;++i) tong[x[i]=s[i]]++;
	for(int i=1;i<range;++i) tong[i]+=tong[i-1];
	for(int i=n-1;i>=0;--i) sa[--tong[x[i]]]=i;
	for(int k=1;k<=n;k<<=1)
	  {
	  	int 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;
		memset(tong,0,sizeof(int)*range);
		for(int i=0;i<n;++i) tong[x[y[i]]]++;
		for(int i=1;i<range;++i) tong[i]+=tong[i-1];
		for(int i=n-1;i>=0;--i) sa[--tong[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,i,k)?p-1:p++;
	  	if(p>=n) break;
	  	range=p;
	  }
}
void get_lcp()
{
	int k=0;
	for(int i=0;i<n;++i) rank[sa[i]]=i;
	for(int i=0;i<n;++i) if(rank[i])
	  {
	  	if(k) --k;
	  	int j=sa[rank[i]-1];
	  	while(s[i+k]==s[j+k]) ++k;
	  	lcp[rank[i]]=k;
	  }
}
bool check(int x)
{
	int cnt=1;
    for(int i=1;i<=n;++i)
      {
        if(lcp[i]<x||i==n)
          {
            if(cnt>=K) return 1;
            cnt=1;
          }
        else if(lcp[i]>=x) ++cnt;
      }
    return 0;
}
int main()
{
	scanf("%d%d",&n,&K);
	for(int i=0;i<n;++i)
	  {
	  	scanf("%d",&T[i].v);
	  	T[i].p=i;
	  }
	sort(T,T+n);
    for(int i=1;i<n;++i)
      {
        if(T[i].v!=T[i-1].v) ++zy;
        s[T[i].p]=zy-1;
      }
    s[n]=-1;
    build_sa(zy);
    get_lcp();
    int l=0,r=n;
    while(r>l)
      {
      	int mid=(l+r+1>>1);
      	if(check(mid)) l=mid;
      	else r=mid-1;
      }
    printf("%d\n",l);
	return 0;
}

【后缀数组】【二分答案】poj3261

标签:

原文地址:http://www.cnblogs.com/autsky-jadek/p/4462156.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!