还是后缀数组,我觉得用后缀数组做字符串的题真真无敌了,今天做字符串练习时,又学到一项新技能,RMQ,加上题目的思路比较巧,就随意做一下总结。。
题目链接:http://poj.org/problem?id=3693
题目大意是给你一个串,要你求重复次数最多的子串
题目的主要思路还是网上的(我还是太菜。。)
给个思路的链接(我觉得讲的挺好):http://m.blog.csdn.net/blog/yanglei040/21179865
大概意思是:遍历所有可能长度,然后进行分段,对相邻的段的端口进行前后匹配,通过匹配长度来确定该匹配的子串最大重复数,看图示挺好理解的
下面说一下RMQ:RMQ使用来求一个范围内的最小值,思想类似于归并排序,就是先两两比较,再用结果进行两两比较,一直递推下去,用一个二维数组存储,能得到任意位置起点的长度为2的幂次范围的最小值
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int str[100005],sa[100005],Rank[100005],height[100005],a[100005],b[100005],c[100005],index,len,time;
int RMQ[100005][20];
char temp[100005];
int char2int()
{
int len=strlen(temp);
for(int i=0;i<len;i++)
str[i]=(int)temp[i];
str[len]=0;
return len;
}
void DA(int top,int m)
{
int *x=a,*y=b;
for(int i=0;i<m;i++)
c[i]=0;
for(int i=0;i<top;i++)
c[x[i]=str[i]]++;
for(int i=1;i<m;i++)
c[i]+=c[i-1];
for(int i=top-1;i>=0;i--)
sa[--c[x[i]]]=i;
for(int k=1;k<=top;k*=2)
{
int p=0;
for(int i=top-k;i<top;i++)
y[p++]=i;
for(int i=0;i<top;i++)
if(sa[i]>=k)
y[p++]=sa[i]-k;
for(int i=0;i<m;i++)
c[i]=0;
for(int i=0;i<top;i++)
c[x[y[i]]]++;
for(int i=1;i<m;i++)
c[i]+=c[i-1];
for(int i=top-1;i>=0;i--)
sa[--c[x[y[i]]]]=y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for(int i=1;i<top;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p-1:p++;
if(p>=top)
break;
m=p;
}
}
void getHeight(int top)
{
for(int i=1;i<=top;i++)
Rank[sa[i]]=i;
int t=0;
for(int i=0;i<top;i++)
{
if(t>0)
t--;
int j=sa[Rank[i]-1];
while(str[i+t]==str[j+t])
t++;
height[Rank[i]]=t;
}
}
void init_RMQ(int top)//RMQ[j][i]中j代表起始位置,i代表2^i的长度
{
for(int i=1;i<=top;i++)
RMQ[i][0]=height[i];
for(int i=1;(1<<i)<=top;i++)
for(int j=1;(j+(1<<i)-1)<=top;j++)
RMQ[j][i]=min(RMQ[j][i-1],RMQ[j+(1<<(i-1))][i-1]);//利用递推的思想
}
int Query(int l,int r)
{
if(l>r)
swap(l,r);
l++;//排名l和r的后缀的lcp等于min(height[l+1~r]),所以l++
int k=0;
while((1<<(k+1))<=r-l+1)
k++;//增加k值使得整段被覆盖
return min(RMQ[l][k],RMQ[r-(1<<k)+1][k]);//拆成两段
}
void getIndex(int top)
{
index=sa[1];
time=1;
len=1;
for(int l=1;l<=top/2;l++)
{
for(int i=0;(i+1)*l<top;i++)
{
int k=Query(Rank[i*l],Rank[(i+1)*l]);
int j=1,pos=i*l;//pos代表如果当前查询就是最结果时的起始下标
while(i*l-j>=0&&str[i*l-j]==str[(i+1)*l-j]&&j<l)//j<l控制不要跨段
{
k++;
if(k%l==0)//表示k/l+1的值增加了1,这时pos一定要更新到第一个下标
pos=i*l-j;
if(Rank[i*l-j]<Rank[pos])//向左匹配时,还要保证字典序尽量小
pos=i*l-j;
j++;
}
if(k/l+1>time||(k/l+1==time&&Rank[index]>Rank[pos]))
{
index=pos;
time=k/l+1;
len=time*l;
}
}
}
}
int main()
{
int t=1;
while(1)
{
scanf("%s",temp);
if(temp[0]=='#')
break;
int top=char2int();
DA(top+1,150);
getHeight(top);
init_RMQ(top);
getIndex(top);
printf("Case %d: ",t++);
for(int i=index;i<index+len;i++)
printf("%c",temp[i]);
printf("\n");
}
return 0;
}