重复次数最多的连续重复子串(spoj687,pku3693)
给定一个字符串,求重复次数最多的连续重复子串。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
const int oo=1073741819;
int sa[105000],wv[105000],ws[105000],rk[105000],ht[105000],f[105000][18],y[105000],i,l,d[18],st[105000];
int max,t;
char s[105000];
void dag(int l,int m)
{
int i,j,p;
for (i=1;i<=m;i++) ws[i]=0;
for (i=1;i<=l;i++) ws[rk[i]=s[i]]++;
for (i=1;i<=m;i++) ws[i]+=ws[i-1];
for (i=l;i>=1;i--) sa[ws[rk[i]]--]=i;
for (j=1,p=0;p<l;j<<=1,m=p)
{
for (p=0,i=l-j+1;i<=l;i++) y[++p]=i;
for (i=1;i<=l;i++) if (sa[i]>j) y[++p]=sa[i]-j;
for (i=1;i<=l;i++) wv[i]=rk[y[i]];
for (i=1;i<=m;i++) ws[i]=0;
for (i=1;i<=l;i++) ws[wv[i]]++;
for (i=1;i<=m;i++) ws[i]+=ws[i-1];
for (i=l;i>=1;i--) sa[ws[wv[i]]--]=y[i];
for (i=1;i<=l;i++) y[i]=rk[i];
for (rk[sa[1]]=1,p=1,i=2;i<=l;i++)
rk[sa[i]]=((y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+j]==y[sa[i-1]+j])) ? p : ++p;
}
}
void hig(int l)
{
int i,j,p;
for (p=0,i=1;i<=l;ht[rk[i++]]=p)
for (p ? --p : 0,j=sa[rk[i]-1];s[j+p]==s[i+p];++p) ;
}
int min(int x,int y)
{
return (x<y) ? x : y;
}
void origin()
{
int k,i,j,p;
memset(f,127,sizeof(f));
k=(int)floor(log((double)l)/log((double)2));
d[0]=1;
for (i=1;i<=k;i++) d[i]=d[i-1]<<1;
for (j=1;j<=l;j++) f[j][0]=ht[j+1];
for (j=1;j<=k;j++)
for (p=1;p<=l;p++)
if (p+d[j]-1<=l)
{
f[p][j]=min(f[p][j-1],f[p+d[j-1]][j-1]);
}
}
int ask(int l,int r)
{
int k,u,e;
if (l>r) {e=l,l=r,r=e;}
r--;
u=(int)floor(log((double)r-l+1)/log((double)2));
k=min(f[l][u],f[r-d[u]+1][u]);
return k;
}
void getout()
{
int i,j1,j2,j,k,p;
for (i=1;i<=l;i++)
{
j1=sa[i];
for (j=1;j<=t;j++)
{
j2=j1+st[j];
if (j2<=l)
{
k=ask(rk[j1],rk[j2]);
int kk=(k/st[j])+1;
if (kk==max)
{
for (kk=1;kk<=max;kk++)
for (p=j1;p<j2;p++) printf("%c",s[p]);
printf("\n");
return ;
}
}
}
}
}
void work()
{
int j,k,ll,j1,i;
t=0,max=-oo;
for (i=1;i<=l;i++)
{
for (j=1;j+i<=l;j+=i)
{
j1=j+i;
k=ask(rk[j],rk[j1]);
// if (k<i) continue;
ll=(k/i)+1;
if (ll>max)
{
max=ll;
st[t=1]=i;
}
else if (ll==max)
{
st[++t]=i;
}
ll=(i-k%i);
ll=j-ll;
if (ll<=0) continue;
j1=ll+i;
k=ask(rk[ll],rk[j1]);
if (k<i) continue;
ll=k/i+1;
if (ll>max)
{
max=ll;
st[t=1]=i;
}
else if (ll==max)
{
st[++t]=i;
}
}
}
if (max>0) getout();
else {
char ans='z';
for (i=1;i<=l-1;i++)
if (s[i]<ans) ans=s[i];
printf("%c\n",ans);
}
}
void init()
{
l=strlen(s+1);
s[++l]='$';
dag(l,255);
hig(l);
origin();
work();
}
int main()
{
i=0;
for (;;)
{
memset(s,0,sizeof(s));
scanf("%s\n",s+1);
if ('#'==s[1]) break;
++i;
printf("Case %d: ",i);
init();
}
return 0;
}
长度不小于 k 的公共子串的个数(pku3415)给定两个字符串 A 和 B,求长度不小于 k 的公共子串的个数(可以相同) 。
考虑对于一个处于B的后缀,如何快速统计它前面处于A的后缀与他满足要求的子串,用维护一个单增栈,如果插入元素<=栈顶,则栈顶可被划分到要插入的元素,因为最长公共前缀由最小值确定,每个元素储存覆盖区间大小及合法子串和,对于处于A的后缀也做一遍。
#include <cstdio>
#include <cstdlib>
#include <cstring>
char a[300000],b[100500];
int la,lb,n,k,pow,t;
int ws[300000],wv[300000],sa[300000],rk[300000],y[300000],ht[300000];
long long ans,size[300000],sum[300000],c[300000],tot,s[300000];
long long min(long long x,long long y) {return (x<y) ? x : y;}
void dag(int l,int m)
{
int i,j,p;
for (i=1;i<=m;i++) ws[i]=0;
for (i=1;i<=l;i++) ws[rk[i]=a[i]]++;
for (i=1;i<=m;i++) ws[i]+=ws[i-1];
for (i=l;i>=1;i--) sa[ws[rk[i]]--]=i;
for (j=1,p=0;p<l;j<<=1,m=p)
{
for (p=0,i=l-j+1;i<=l;i++) y[++p]=i;
for (i=1;i<=l;i++) if (sa[i]>j) y[++p]=sa[i]-j;
for (i=1;i<=l;i++) wv[i]=rk[y[i]];
for (i=1;i<=m;i++) ws[i]=0;
for (i=1;i<=l;i++) ws[wv[i]]++;
for (i=1;i<=m;i++) ws[i]+=ws[i-1];
for (i=l;i>=1;i--) sa[ws[wv[i]]--]=y[i];
for (i=1;i<=l;i++) y[i]=rk[i];
for (rk[sa[1]]=1,p=1,i=2;i<=l;i++)
rk[sa[i]]=((y[sa[i]]==y[sa[i-1]])&&(y[sa[i]+j]==y[sa[i-1]+j])) ? p : ++p;
}
}
void hig(int l)
{
int i,j,p;
for (p=0,i=1;i<=l;ht[rk[i++]]=p)
for (p ? --p : p,j=sa[rk[i]-1];a[j+p]==a[i+p];++p) ;
for (i=1;i<=l;i++) ht[i]=ht[i+1];
}
void updata(int x,int y)
{
tot=tot-sum[y]+size[y]*(c[x]-k+1);
c[y]=c[x],size[y]+=size[x],sum[y]=(c[y]-k+1)*size[y];
}
void init()
{
int i;
scanf("%s\n",a+1),scanf("%s\n",b+1);
la=strlen(a+1),a[++la]='$',lb=strlen(b+1),pow=la;
for (i=1;i<=lb;i++) a[++la]=b[i];
dag(la,'z');hig(la);
tot=0,ans=0,t=0;
for (i=2;(sa[i]>pow)||(ht[i]<k);i++) ;
c[t=1]=ht[i],size[t]=1,sum[t]=c[t]-k+1,tot=sum[t];
for (i++;i<=la;i++)
{
if (sa[i]>pow) {
ans+=tot;
if (0==t) continue;
if (ht[i]<k) {t=0,tot=0;continue;}
tot-=sum[t],c[t]=min(c[t],ht[i]),sum[t]=(c[t]-k+1)*size[t],tot+=sum[t];
for (;(t>1)&&(c[t]<=c[t-1]);t--) updata(t,t-1);
}
else {
c[++t]=ht[i],size[t]=1,sum[t]=c[t]-k+1,tot+=sum[t];
for (;(t>1)&&(c[t]<=c[t-1]);t--) updata(t,t-1);
if (c[t]<k) t=0,tot=0;
}
}
memset(size,0,sizeof(size)),memset(sum,0,sizeof(sum)),memset(c,0,sizeof(c));
tot=0,t=0;
for (i=2;(sa[i]<pow)||(ht[i]<k);i++) ;
c[t=1]=ht[i],size[t]=1,sum[t]=c[t]-k+1,tot=sum[t];
for (i++;i<=la;i++)
{
if (sa[i]<pow) {
ans+=tot;
if (0==t) continue;
if (ht[i]<k) {t=0,tot=0;continue;}
tot-=sum[t],c[t]=min(c[t],ht[i]),sum[t]=(c[t]-k+1)*size[t],tot+=sum[t];
for (;(t>1)&&(c[t]<=c[t-1]);t--) updata(t,t-1);
}
else {
c[++t]=ht[i],size[t]=1,sum[t]=c[t]-k+1,tot+=sum[t];
for (;(t>1)&&(c[t]<=c[t-1]);t--) updata(t,t-1);
if (c[t]<k) t=0,tot=0;
}
}
printf("%I64d\n",ans);
}
int main()
{
freopen("3415.in","r",stdin);
freopen("3415.out","w",stdout);
for (;;) {
scanf("%d\n",&k);
if (!k) break;
init();
}
return 0;
}