您的位置:首页 > 其它

hdu 4691 最长的共同前缀 后缀数组 +lcp+rmq

2015-07-23 21:29 399 查看
http://acm.hdu.edu.cn/showproblem.php?

pid=4691

去年夏天,更多的学校的种族称号。当时,没有后缀数组

今天将是,事实上,自己的后缀阵列组合rmq或到,但是,题意理解的一个问题,再折腾了很长时间,,,,

此处简单解释下题目例子吧,希望对读者有帮助 以最后一组数据为例

myxophytamyxopodnabnabbednabbingnabit

6

0 9

9 16

16 19

19 25

25 32

32 37

前两行不解释,题目叙述非常清楚

从第三行,0 9 指的是第一个字符串是从第一行的字符串的0-9 左闭右开,

下面5行同样

继续看题目的正文叙述的例子

那么压缩之后的第一个就是“0空格以及前9个字符外加一个换行”一共12个。下面几行相同的算法

注意假设公共前缀长度是24,那么按两个单元存储,这就是我写的Weishu函数的作用

上代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN =100000+20;

int n,k;//n=strlen(s);
int Rank[MAXN],tmp[MAXN],d[MAXN],st[MAXN][20],lcp[MAXN],sa[MAXN];
char s[MAXN];

/*使用Rank对sa排序*/
bool cmpSa(int i, int j)
{
if(Rank[i] != Rank[j])return Rank[i] < Rank[j];
else
{   /*以下的Rank[t],已经是以t开头长度小于等于k/2的。
sa[i]的名次,仅仅是以i开头的后缀,而长度不同*/
int ri = i+k <=n? Rank[i+k]:-1;
int rj = j+k <= n ? Rank[j+k]:-1;
return ri <rj;
}
}

/*计算SA*/
void consa(char *s, int *sa)
{
for(int i=0;i<=n;i++){
sa[i]=i;Rank[i] = i < n?s[i]:-1;
}

for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1開始,由于0*2=0*/
{
sort(sa,sa+n+1,cmpSa);
tmp[sa[0]] = 0; /*此时tmp仅仅是暂存rank*/
for(int i=1;i<=n;i++){
tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);
}
for(int i=0;i<=n;i++){
Rank[i] = tmp[i];
}
}
}

void construct_lcp(char *s,int *sa,int *lcp)
{
//n=strlen(s);
for(int i=0; i<=n; i++)Rank[sa[i]]=i;

int h=0;
lcp[0]=0;
for(int i=0;i<n;i++)
{
int j=sa[Rank[i]-1];

if(h>0)h--;
for(; j+h<n && i+h<n; h++)
{
if(s[j+h]!=s[i+h])break;
}
lcp[Rank[i]-1]=h;
}
}

void InitRMQ(int nn)
{
int i,j;
for(d[0]=1,i=1;i<21;i++)d[i]=2*d[i-1];
for(i=0;i<nn;i++)st[i][0]=lcp[i];
///////////////////////////////
// for(int i=0;i<nn;i++)
//{
//      printf("%s i=%d sa=%d rank=%d lcp=%d\n",s+sa[i],i,sa[i],Rank[i],lcp[i]);
//      printf("||||||||%s lcp[rank]=%d\n",s+i,lcp[Rank[i]]);
//  }
////////////////////////////////

int k=int( log(double(nn))/log(2.0)+1 );
for(j=1;j<k;j++)
for(i=0;i<nn;i++)
{
if(i+d[j-1]-1<nn)
{
st[i][j]=min(st[i][j-1],st[i+d[j-1]][j-1]);
}
else break;
}
}

int weishu(int n)
{
if(n<10)return 1;
int ans=0;
while(n)
{
n/=10;
ans++;
}
return ans;
}

int main()
{
//freopen("hdu4691.txt","r",stdin);
long long ansb,ansa;
int kk;
while(~scanf("%s",s))
{
ansb=ansa=0;
n=strlen(s);
consa(s,sa);
construct_lcp(s,sa,lcp);

InitRMQ(n+1);
int num,l,r,x,y;
scanf("%d",&num);
int last=0,lastlen=0;
scanf("%d%d",&l,&r);
ansb+=r-l+1;
ansa+=r-l+3;
lastlen=r-l;
last=l;
for(int i=1;i<num;i++)
{
scanf("%d%d",&l,&r);
ansb+=r-l+1;
ansa+=r-l;
x=min(Rank[last],Rank[l]),y=max(Rank[last],Rank[l])-1;
kk = int( log(double(y-x+1))/log(2.0) );
int ret;
if(l == last)ret=n-l;
else ret=min(st[x][kk], st[y-d[kk]+1][kk]) ;
ret=min(ret, min(lastlen,r-l));
ansa =ansa-ret+weishu(ret)+2;
last=l;
lastlen=r-l;
}
printf("%I64d %I64d\n",ansb,ansa);
}
return 0;
}


再加一个rmq+后缀数组求最长公共前缀的模板吧

(事实上还没有測试,遇到题在測试)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <cmath>

using namespace std;

const int MAXN =100000+20;

int n,k;//n=strlen(s);
int Rank[MAXN],tmp[MAXN],d[MAXN],st[MAXN][20],lcp[MAXN],sa[MAXN];
char s[MAXN];

/*使用Rank对sa排序*/
bool cmpSa(int i, int j)
{
if(Rank[i] != Rank[j])return Rank[i] < Rank[j];
else
{   /*以下的Rank[t],已经是以t开头长度小于等于k/2的,
sa[i]的名次。仅仅是以i开头的后缀,而长度不同*/
int ri = i+k <=n? Rank[i+k]:-1;
int rj = j+k <= n ? Rank[j+k]:-1;
return ri <rj;
}
}

/*计算SA*/
void consa(char *s, int *sa)
{
for(int i=0;i<=n;i++){
sa[i]=i;Rank[i] = i < n?s[i]:-1;
}

for(k=1;k<=n;k*=2)/*注意此代码中k是全局变量 别乱用,循环必须从1開始。由于0*2=0*/
{
sort(sa,sa+n+1,cmpSa);
tmp[sa[0]] = 0; /*此时tmp仅仅是暂存rank*/
for(int i=1;i<=n;i++){
tmp[sa[i]] = tmp[sa[i-1]] +(cmpSa(sa[i-1],sa[i])?1:0);
}
for(int i=0;i<=n;i++){
Rank[i] = tmp[i];
}
}
}

void construct_lcp(char *s,int *sa,int *lcp)
{
//n=strlen(s);
for(int i=0; i<=n; i++)Rank[sa[i]]=i;

int h=0;
lcp[0]=0;
for(int i=0;i<n;i++)
{
int j=sa[Rank[i]-1];

if(h>0)h--;
for(; j+h<n && i+h<n; h++)
{
if(s[j+h]!=s[i+h])break;
}
lcp[Rank[i]-1]=h;
}
}

void InitRMQ(int nn)
{
int i,j;
for(d[0]=1,i=1;i<21;i++)d[i]=2*d[i-1];
for(i=0;i<nn;i++)st[i][0]=lcp[i];
int k=int( log(double(nn))/log(2.0)+1 );
for(j=1;j<k;j++)
for(i=0;i<nn;i++)
{
if(i+d[j-1]-1<nn)
{
st[i][j]=min(st[i][j-1],st[i+d[j-1]][j-1]);
}
else break;
}
}

int ansLcp(int a, int b)
{
int kk;
if(a == b)return n-a;//n是整个字符串的长度
x=min(Rank[a],Rank[b]),y=max(Rank[a],Rank[b])-1;
kk = int( log(double(y-x+1))/log(2.0) );
return min(st[x][kk], st[y-d[kk]+1][kk]) ;
}

int Query(int Q)//Q次询问
{
int ans;
for(int i=0;i<Q;i++)
{
scanf("%d%d",&a,&b);
ans=ansLcp(a,b);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: