您的位置:首页 > 其它

POJ-3693-Maximum repetition substring(后缀数组-重复次数最多的连续重复子串)

2013-09-06 21:54 615 查看
题意:

给出一个串,求重复次数最多的连续重复子串

分析:

比较容易理解的部分就是枚举长度为L,然后看长度为L的字符串最多连续出现几次。

既然长度为L的串重复出现,那么str[0],str[l],str[2*l]……中肯定有两个连续的出现在字符串中。

那么就枚举连续的两个,然后从这两个字符前后匹配,看最多能匹配多远。

即以str[i*l],str[i*l+l]前后匹配,这里是通过查询suffix(i*l),suffix(i*l+l)的最长公共前缀

通过rank值能找到i*l,与i*l+l的排名,我们要查询的是这段区间的height的最小值,通过RMQ预处理

达到查询为0(1)的复杂度,

设LCP长度为M, 则答案显然为M / L + 1, 但这不一定是最好的, 因为答案的首尾不一定再我们枚举的位置上. 我的解决方法是, 我们考虑M % L的值的意义, 我们可以认为是后面多了M % L个字符, 但是我们更可以想成前面少了(L - M % L)个字符! 所以我们求后缀j * L - (L - M % L)与后缀(j + 1) * L - (L - M % L)的最长公共前缀。

即把之前的区间前缀L-M%L即可。

分析引自/article/2566059.html

// File Name: 3693.cpp
// Author: Zlbing
// Created Time: 2013年09月06日 星期五 21时05分32秒

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<set>
#include<map>
#include<vector>
#include<cstring>
#include<stack>
#include<cmath>
#include<queue>
using namespace std;
#define CL(x,v); memset(x,v,sizeof(x));
#define INF 0x3f3f3f3f
#define LL long long
#define REP(i,r,n) for(int i=r;i<=n;i++)
#define RREP(i,n,r) for(int i=n;i>=r;i--)
//rank从0开始
//sa从1开始,因为最后一个字符(最小的)排在第0位
//height从2开始,因为表示的是sa[i-1]和sa[i]
const int MAXN=220000;
int rank[MAXN],sa[MAXN],X[MAXN],Y[MAXN],height[MAXN];
char s[MAXN];
int buc[MAXN];
void calheight(int n) {
int i , j , k = 0;
for(i = 1 ; i <= n ; i++) rank[sa[i]] = i;
for(i = 0 ; i < n ; height[rank[i++]] = k)
for(k?k--:0 , j = sa[rank[i]-1] ; s[i+k] == s[j+k] ; k++);
}
bool cmp(int *r,int a,int b,int l) {
return (r[a] == r[b] && r[a+l] == r[b+l]);
}
void suffix(int n,int m = 128) {
int i , l , p , *x = X , *y = Y;
for(i = 0 ; i < m ; i ++) buc[i] = 0;
for(i = 0 ; i < n ; i ++) buc[ x[i] = s[i]  ] ++;
for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[i] ]] = i;
for(l = 1,p = 1 ; p < n ; m = p , l *= 2) {
p = 0;
for(i = n-l ; i < n ; i ++) y[p++] = i;
for(i = 0 ; i < n ; i ++) if(sa[i] >= l) y[p++] = sa[i] - l;
for(i = 0 ; i < m ; i ++) buc[i] = 0;
for(i = 0 ; i < n ; i ++) buc[ x[y[i]] ] ++;
for(i = 1 ; i < m ; i ++) buc[i] += buc[i-1];
for(i = n - 1; i >= 0 ; i --) sa[ --buc[ x[y[i]] ] ] = y[i];
for(swap(x,y) , x[sa[0]] = 0 , i = 1 , p = 1 ; i < n ; i ++)
x[ sa[i] ] = cmp(y,sa[i-1],sa[i],l) ? p-1 : p++;
}
calheight(n-1);//后缀数组关键是求出height,所以求sa的时候顺便把rank和height求出来
}
//当需要反复询问两个后缀的最长公共前缀时用到RMQ
int Log[MAXN];
int best[20][MAXN];
void initRMQ(int n) {//初始化RMQ
for(int i = 1; i <= n ; i ++) best[0][i] = height[i];
for(int i = 1; i <= Log
; i ++) {
int limit = n - (1<<i) + 1;
for(int j = 1; j <= limit ; j ++) {
best[i][j] = min(best[i-1][j] , best[i-1][j+(1<<i>>1)]);
}
}
}
int lcp(int a,int b) {//询问a,b后缀的最长公共前缀
a = rank[a];    b = rank[b];
if(a > b) swap(a,b);
a ++;
int t = Log[b - a + 1];
return min(best[t][a] , best[t][b - (1<<t) + 1]);
}
int main() {
//预处理每个数字的Log值,常数优化,用于RMQ
Log[0] = -1;
for(int i = 1; i < MAXN ; i ++) {
Log[i] = (i&(i-1)) ? Log[i-1] : Log[i-1] + 1 ;
}
int cas=0;
//*******************************************
//    n为数组长度,下标0开始
//    将初始数据,保存在s里,并且保证每个数字都比0大
//    m = max{ s[i] } + 1
//    一般情况下大多是字符操作,所以128足够了
//*******************************************
while(~scanf("%s",s))
{
if(s[0]=='#')break;
int n=strlen(s);
s
=0;
suffix(n+1);
initRMQ(n);
int maxx=0;
int len=0;
int pos=0;
for(int i=1;i<=n/2;i++)
{
for(int j=0;j+i<n;j+=i)
{
if(s[j]!=s[j+i])continue;
int k=lcp(j,j+i);
int tot=k/i+1;
int t=i-(k%i);
int p=j;
int cnt=0;
if(t&&t<i)
{
for(int m=j-1;m>j-i&&s[m]==s[m+i]&&m>=0;m--)
{
cnt++;
if(cnt==t)
{
tot++;
p=m;
}
else if(rank[p]>rank[m])
{
p=m;
}
}
}
if(tot>maxx)
{
maxx=tot;
pos=p;
len=maxx*i;
}
else if(tot==maxx)
{
if(rank[p]<rank[pos])
{
pos=p;
len=maxx*i;
}
}
}
}
printf("Case %d: ",++cas);
if(maxx<2)
{
char ch='z';
for(int i=0;i<n;i++)
if(s[i]<ch)
ch=s[i];
printf("%c\n",ch);
continue;
}
for(int i=pos;i<pos+len;i++)
printf("%c",s[i]);
printf("\n");
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐