您的位置:首页 > 其它

ZOJ Monthly, July 2015 K-hash 后缀数组去重做法

2015-07-27 12:05 363 查看
题意:意思有一个数字串,然后问子串构成的数字,有几个模k为0,1,2...k-1的。然后要求数字不能重复,也不能有前导零

做法:

先不考虑0的问题。

(1)首先o(32n)计算出所有的模k为0,1,2...k-1的子串有几个。此时不考虑重复。即每次计算出到第i位位置,每种串有多少个。然后把第i个位置插进去就能算了。

(2)然后利用后缀数组去重。如果height[i]=j那么表示第i个串与第i-1个串有j个子串相同,把这j个串的值从之前计算出来的答案中减去就好了。这个过程如果暴力的话是n^2,我开个sum数组是每次把前次的结果记录下来。这样如果height[i+1]比height[i]要大的话,比多出来的串的值加到sum数组里面去,然后ans减去sum即可。。。

考虑0

在(1)中不插入0做头位置,在(2)中不去首位置为0的重。即可。

PS:要预处理一个哈希数组,这样可以o(1)得到i到j这一段子串构成的数字的值。

PSS:关于这个做法的复杂度,我认为是o(32n)左右,但是我并不会证明,因为形式上看起来好像是o(n^2)。关键在于height数组的上下浮动是否是o(n)级别的。。。。这个代码在zoj上跑了120ms,比赛中提交的代码里是最快了。。。。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define maxn 55555
using namespace std;

int wa[maxn],wb[maxn],wv[maxn],wsa[maxn];
int ranks[maxn],height[maxn];

void calheight(int *r,int *sa,int n)
{
int i,j,k=0;
for(i=1;i<=n;i++) ranks[sa[i]]=i;
for(i=0;i<n ;height[ranks[i++]]=k)
for(k?k--:0,j=sa[ranks[i]-1];r[i+k]==r[j+k];k++);
return;
}

int cmp(int *r,int a,int b,int l)
{
return r[a]==r[b]&&r[a+l]==r[b+l];
}

void build_sa(int *r,int *sa,int n,int m)
{
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<m;i++) wsa[i]=0;
for(i=0;i<n;i++) wsa[x[i]=r[i]]++;
for(i=1;i<m;i++) wsa[i]+=wsa[i-1];
for(i=n-1;i>=0;i--) sa[--wsa[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,m=p)
{
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<m;i++) wsa[i]=0;
for(i=0;i<n;i++) wsa[wv[i]]++;
for(i=1;i<m;i++) wsa[i]+=wsa[i-1];
for(i=n-1;i>=0;i--) sa[--wsa[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}
int r[maxn],sa[maxn];
char str[55555];
int hash[55555],shi[55555];
int pan(char ch)
{
return ch-'0';
}
int ans[111],k;
void INIT()
{
int sum[100],add[111];
bool ling=false;
memset(sum,0,sizeof(sum));
memset(add,0,sizeof(add));
memset(ans,0,sizeof(ans));
int len=strlen(str);
for(int i=0;i<len;i++)
{
int dig=pan(str[i]);
//	dig%=k;
for(int j=k-1;j>=0;j--)
{
add[(j*10+dig)%k]+=sum[j];
}
if(dig!=0)
add[dig%k]++;
else ling=true;
for(int j=0;j<=k;j++)
{
ans[j]+=add[j];
sum[j]=add[j];
add[j]=0;
}
}
if(ling)ans[0]++;
}

int get_hash(int i,int j)
{
if(i==0)return hash[j-1];
int hi=hash[i-1];
int hj=hash[i+j-1];
hi=hi*shi[j];
hi%=k;
int det=hj-hi;
while(det<0)det+=k;
return det;
}
int main(){
shi[0]=1;

while(scanf("%s%d",str,&k)!=EOF){
int n=strlen(str);
for(int i=1;i<=n;i++)shi[i]=shi[i-1]*10%k;
for(int i=0;str[i];i++)
r[i]=(int)str[i];
r
=0;
build_sa(r,sa,n+1,130);
calheight(r,sa,n);//height(0-n)
hash[0]=pan(str[0])%k;
for(int i=1;i<n;i++)
{
hash[i]=hash[i-1]*10+pan(str[i]);
hash[i]%=k;
}
INIT();
int last=0;
int sum[33];
memset(sum,0,sizeof(sum));
int lf=-1;
for(int i=2;i<=n;i++)
{
int first=sa[i];
if(height[i]>last)
{
for(int j=last+1;j<=height[i];j++)
{
int o=get_hash(first,j);
sum[o]++;
}
}
else
{
for(int j=last;j>height[i];j--)
{
int o=get_hash(lf,j);
sum[o]--;
}
}
last=height[i];
if(str[first]!='0')
for(int j=0;j<k;j++)
{
ans[j]-=sum[j];
}
lf=first;
}
int  key=0;

for(int i=0;i<k;i++)
{
printf("%d",ans[i]);
if(i==k-1)printf("\n");
else printf(" ");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: