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

注意:对整型数组求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;
}
时间: 2024-10-07 18:42:08

【后缀数组】【二分答案】poj3261的相关文章

POJ 1743 Musical Theme(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=1743 [题目大意] 给出一首曲子的曲谱,上面的音符用不大于88的数字表示, 现在请你确定它主旋律的长度,主旋律指的是出现超过一次, 并且长度不小于5的最长的曲段,主旋律出现的时候并不是完全一样的, 可能经过了升调或者降调,也就是说, 是原来主旋律所包含的数字段同时加上或者减去一个数所得, 当然,两段主旋律之间也是不能有重叠的,现在请你求出这首曲子主旋律的长度, 如果不存在请输出0. [题解] 首先要处理的是升调和降调的问题,由

POJ 3080 Blue Jeans(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=3080 [题目大意] 求k个串的最长公共子串,如果存在多个则输出字典序最小,如果长度小于3则判断查找失败. [题解] 将所有字符串通过拼接符拼成一个串,做一遍后缀数组,二分答案,对于二分所得值,将h数组大于这个值的相邻元素分为一组,判断组内元素是否覆盖全字典,是则答案成立,对于答案扫描sa,输出第一个扫描到的子串即可. [代码] #include <cstdio> #include <cstring> #inclu

POJ 3294 Life Forms(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=3294 [题目大意] 求出在至少在一半字符串中出现的最长子串. 如果有多个符合的答案,请按照字典序输出. [题解] 将所有的字符串通过不同的拼接符相连,作一次后缀数组, 二分答案的长度,然后在h数组中分组,判断是否可行, 按照sa扫描输出长度为L的答案即可.注意在一个子串中重复出现答案串的情况. [代码] #include <cstdio> #include <cstring> #include <vecto

POJ 3261 Milk Patterns(后缀数组+二分答案)

[题目链接] http://poj.org/problem?id=3261 [题目大意] 求最长可允许重叠的出现次数不小于k的子串. [题解] 对原串做一遍后缀数组,二分子串长度x,将前缀相同长度超过x的后缀分组, 如果存在一个大小不小于k的分组,则说明答案可行,分治得到最大可行解就是答案. [代码] #include <cstdio> #include <cstring> #include <vector> using namespace std; const int

POJ3294--Life Forms 后缀数组+二分答案 大于k个字符串的最长公共子串

Life Forms Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 10800   Accepted: 2967 Description You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial traits such as height, colour, wrinkles, e

SPOJ 220 Relevant Phrases of Annihilation(后缀数组+二分答案)

[题目链接] http://www.spoj.pl/problems/PHRASES/ [题目大意] 求在每个字符串中出现至少两次的最长的子串 [题解] 注意到这么几个关键点:最长,至少两次,每个字符串. 首先对于最长这个条件,我们可以想到二分答案, 然后利用后缀数组所求得的三个数组判断是否满足条件. 其次是出现两次,每次出现这个条件的时候, 我们就应该要想到这是最大值最小值可以处理的, 将出现在同一个字符串中的每个相同字符串的起始位置保存下来, 如果最小值和最大值的差距超过二分长度L,则表明在

poj1743 后缀数组+二分答案

1.给定一个字符串,求最长重复子串,这两个子串可以重叠. 这道题是后缀数组的一个简单应用.做法比较简单,只需要求 height 数组里的最大值即可.2.给定一个字符串,求最长重复子串,这两个子串不能重叠. 这题比上一题稍复杂一点.先二分答案,把题目变成判定性问题:判断是否存在两个长度为 k 的子串是相同的,且不重叠.解决这个问题的关键还是利用height 数组.把排序后的后缀分成若干组,其中每组的后缀之间的 height 值都不小于 k.例如,字符串为“aabaaaab ” ,当 k=2 时,后

cogs249 最长公共子串(后缀数组 二分答案

http://cogs.pro:8080/cogs/problem/problem.php?pid=pxXNxQVqP 题意:给m个单词,让求最长公共子串的长度. 思路:先把所有单词合并成一个串(假设长度是n,包含分隔符),中间用不同符号分隔,求出high[i](表示rk为i的和rk为i+1的后缀的最长公共前缀),然后二分答案ans,对于rk从1扫到n,如果有一段连续的rk值使得high[rk]>=ans且这段的串盖满了每个单词块,那么ans成立,即最终答案大于ans. #include <a

BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) --------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace std; typedef long long

hdu 5008(2014 ACM/ICPC Asia Regional Xi&#39;an Online ) Boring String Problem(后缀数组&amp;二分)

Boring String Problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 219    Accepted Submission(s): 45 Problem Description In this problem, you are given a string s and q queries. For each que