给一个字符串,给定一个字符串,求重复次数最多的连续重复子串。
09年罗穗骞的论文里有讲这题,先引用一下论文里的讲解:
先穷举长度L,然后求长度为L 的子串最多能连续出现几次。首先连续出现1 次是肯定可以的,所以这里只考虑至少2 次的情况。假设在原字符串中连续出现2 次,记这个子字符串为S,那么S 肯定包括了字符r[0], r[L], r[L*2],r[L*3], ……中的某相邻的两个。所以只须看字符r[L*i]和r[L*(i+1)]往前和往后各能匹配到多远,记这个总长度为K,那么这里连续出现了K/L+1 次。最后看最大值是多少。如图7 所示。
穷举长度L 的时间是n,每次计算的时间是n/L。所以整个做法的时间复杂度是O(n/1+n/2+n/3+……+n/n)=O(nlogn)。
以上内容提供了一个思路方向,但剩下的东西也挺难想的...论文里只讲了右面匹配的情况,队向左匹配的方法并没有细说(难道是认为太简单了么....= =.....),举个例子吧(数据是从poj discuss里搬来的)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
b a c c d b a c c d b a c b d b a c b d
加入我现在枚举到了l=5,p1=5,p2=10
令k=LCS(p1,p2)=3(bac),
而重复的次数即c=k/l+1=1;
由于本题要输出字典序最小的答案,所以p1-l+1 -----p1-1之间任何一个位置都可能是我们要的答案,所以我们必须在字符匹配的情况下逐位向前平移去找答案,
移动两位后,p1=3,p2=8,此时k也增加2等于5,发现达到了l,所以此时c++,等于2,之后发现再往前平移字符依然是匹配的,所以继续,找到p1=1,p2=6,此时k=7,c=2,重复的次数和p1在3的时候没有变化,但显然此时的字典序是更小的,所以在这里更新答案的位置,下一位字符不再匹配,所以到此结束。
这样按照论文中的做法,把长度枚举一边即可得到答案。要说一下的是,这么去找最小的字典序的话,最差的情况其实还是n^2的(比如构造50000个a,跑出来结果就要将近10s),可能是数据给的太弱了(网上搜了几个题解,好多代码都被discuss里给的数据cha掉了...),这么写再poj上只用了400ms。我也没什么别的好方法了...还望大神赐教...
spoj687这题就好办了,只要求输出最大的重复次数,和上面的方法一样。枚举长度,然后枚举i*l,(i+1)*l的位置,由于不求字符串,这里我们便可以省去平移。求出k之后,若k%l>0的话,说明p1,p2之前的位置也可能有匹配的情况,所以找到p3=p1-(l-k%l),若此时LCS(p3,p3+l)>k,那么说明前面这l-k%l位也是匹配的,所以k+=(l-k%l)。此时c=k/l+1就是我们要的答案。
贴两个代码了,
poj3693
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=220100;
int s[maxn],ss[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
int sa2[maxn];
char s1[maxn],s2[maxn];
int rank[maxn],height[maxn];
int rank2[maxn],height2[maxn];
int l1,l2;
int len,len1;
int n,m;
inline int idx(char s)
{
return s-'a'+1;
}
void getheight(int n)
{
int i,j,k=0;
for (i=0; i<=n; i++) rank[sa[i]]=i;
for (i=0; i<n; i++)
{
if (k) k--;
int j=sa[rank[i]-1];
while(s[i+k]==s[j+k]) k++;
height[rank[i]]=k;
}
}
void build_ss(int m,int n)
{
n++;
int i,*x=t,*y=t2;
for (int i=0; i<m; i++) c[i]=0;
for (int i=0; i<n; i++) c[x[i]=s[i]]++;
for (int i=1; i<m; i++) c[i]+=c[i-1];
for (int i=n-1; i>=0; i--)
sa[--c[x[i]]]=i;
for (int k=1; k<=n; k<<=1)
{
int p=0;
for (i=n-k; i<n; i++) y[p++]=i;
for (i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;
for (i=0; i<m; i++) c[i]=0;
for (i=0; i<n; i++) c[x[y[i]]]++;
for (i=1; i<m; i++) c[i]+=c[i-1];
for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for (i=1; i<n; i++)
x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])? p-1 : p++;
if (p>=n) break;
m=p;
}
}
int d[maxn][22];
void RMQ_init()
{
for (int i=1; i<=n; i++) d[i][0]=height[i];
for (int j=1; (1<<j)<=n; j++)
for (int i=1; (i+(1<<j)-1)<=n; i++)
d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R)
{
if (L>R) swap(L,R);
L++;
int k=0;
while((1<<(k+1))<=R-L+1) k++;
return min(d[L][k],d[R-(1<<k)+1][k]);
}
int main()
{
// freopen("in.txt","r",stdin);
int tt=0;
while(~scanf("%s",s1) && s1[0]!='#')
{
n=strlen(s1);
for (int i=0; i<n; i++)
s[i]=idx(s1[i]);
s[n]=0;
build_ss(30,n);
getheight(n);
RMQ_init();
int ans=0,pos=-1,len=0;
for (int l=1; l<=n/2; l++)
{
for (int i=0; (i+1)*l<n; i++)
{
int p1=i*l,p2=(i+1)*l;
int k=RMQ(rank[p1],rank[p2]);
int c=k/l+1;
int r=l-k%l;
int p=i*l;
int cnt=0;
for (int j=p1-1; j>p1-l&&s[j]==s[j+l]&&j>=0; j--)
{
cnt++;
if (cnt==r)
{
c++;
p=j;
}
if (rank[j]<rank[p])
p=j;
}
if (ans<c)
{
pos=p;
ans=c;
len=l*c;
}
else if (ans==c)
{
if (rank[pos]>rank[p])
{
pos=p;
len=l*c;
}
}
}
}
cout<<"Case "<<++tt<<": ";
for (int i=0,j=pos; i<len; i++,j++)
cout<<s1[j];
cout<<endl;
}
}
spoj687的话,上面的代码也能过,只不过要跑10s+,若用删掉平移的过程,可以把时间降到4s+
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn=120100;
int s[maxn],ss[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn];
char s1[maxn],s2[maxn];
int rank[maxn],height[maxn];
int l1,l2;
int len,len1;
int n,m;
inline int idx(char s)
{
return s-'a'+1;
}
void getheight(int n)
{
int i,j,k=0;
for (i=0; i<=n; i++) rank[sa[i]]=i;
for (i=0; i<n; i++)
{
if (k) k--;
int j=sa[rank[i]-1];
while(s[i+k]==s[j+k]) k++;
height[rank[i]]=k;
}
}
void build_ss(int m,int n)
{
n++;
int i,*x=t,*y=t2;
for (int i=0; i<m; i++) c[i]=0;
for (int i=0; i<n; i++) c[x[i]=s[i]]++;
for (int i=1; i<m; i++) c[i]+=c[i-1];
for (int i=n-1; i>=0; i--)
sa[--c[x[i]]]=i;
for (int k=1; k<=n; k<<=1)
{
int p=0;
for (i=n-k; i<n; i++) y[p++]=i;
for (i=0; i<n; i++) if (sa[i]>=k) y[p++]=sa[i]-k;
for (i=0; i<m; i++) c[i]=0;
for (i=0; i<n; i++) c[x[y[i]]]++;
for (i=1; i<m; i++) c[i]+=c[i-1];
for (i=n-1; i>=0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x,y);
p=1;
x[sa[0]]=0;
for (i=1; i<n; i++)
x[sa[i]]=(y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k])? p-1 : p++;
if (p>=n) break;
m=p;
}
}
int d[maxn][22];
void RMQ_init()
{
for (int i=1; i<=n; i++) d[i][0]=height[i];
for (int j=1; (1<<j)<=n; j++)
for (int i=1; (i+(1<<j)-1)<=n; i++)
d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
int RMQ(int L,int R)
{
if (L>R) swap(L,R);
L++;
int k=0;
while((1<<(k+1))<=R-L+1) k++;
return min(d[L][k],d[R-(1<<k)+1][k]);
}
int main()
{
// freopen("in.txt","r",stdin);
int tt=0;
scanf("%d",&tt);
while(tt--)
{
scanf("%d",&n);
memset(s1,0,sizeof s1);
for (int i=0; i<n; i++)
scanf("%s",&s1[i]);
n=strlen(s1);
// puts(s1);
for (int i=0; i<n; i++)
s[i]=idx(s1[i]);
s[n]=0;
build_ss(4,n);
getheight(n);
RMQ_init();
int ans=1,len=0;
if (n==0) ans=0;
for (int l=1; l<=n/2; l++)
{
for (int i=0; (i+1)*l<n; i++)
{
int p1=i*l,p2=(i+1)*l;
int k=RMQ(rank[p1],rank[p2]);
int r=l-k%l;
int p3=p1-r;
if (p3>=0 && k%l && RMQ(rank[p3],rank[p3+l])>=k) k+=r;
int c=k/l+1;
ans=max(ans,c);
}
}
cout<<ans<<endl;
}
}