后缀数组第二弹

重复次数最多的连续重复子串(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;
}










                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值