您的位置:首页 > 其它

【poj3415-长度不小于k的公共子串个数】后缀数组+单调栈

2016-09-28 20:05 387 查看


这题曾经用sam打过,现在学sa再来做一遍。

基本思路:计算A所有的后缀和B所有后缀之间的最长公共前缀。

分组之后,假设现在是做B的后缀。前面的串能和当前的B后缀产生的公共前缀必定是从前往后单调递增的,每次与h[i]取min时必定将栈尾一些长的全部取出来,搞成一个短的。

所以就开一个栈,栈里存的是长度,同时存一下它的出现此处cnt。

注意各种细节啊。。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

typedef long long LL;
const int N=2*100010;
int K,sl,cl,sa
,rk
,Rs
,wr
,y
,h
;
LL sk
,cnt
;
char s
,c
;

void get_sa(int m)
{
for(int i=1;i<=cl;i++) rk[i]=c[i];
for(int i=1;i<=m;i++) Rs[i]=0;
for(int i=1;i<=cl;i++) Rs[rk[i]]++;
for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1];
for(int i=cl;i>=1;i--) sa[Rs[rk[i]]--]=i;

int ln=1,p=0;
while(p<cl)
{
int k=0;
for(int i=cl-ln+1;i<=cl;i++) y[++k]=i;
for(int i=1;i<=cl;i++) if(sa[i]>ln) y[++k]=sa[i]-ln;

for(int i=1;i<=cl;i++) wr[i]=rk[y[i]];
for(int i=1;i<=m;i++) Rs[i]=0;
for(int i=1;i<=cl;i++) Rs[wr[i]]++;
for(int i=1;i<=m;i++) Rs[i]+=Rs[i-1];
for(int i=cl;i>=1;i--) sa[Rs[wr[i]]--]=y[i];

for(int i=1;i<=cl;i++) wr[i]=rk[i];
for(int i=cl+1;i<=cl+ln;i++) wr[i]=0;
p=1;rk[sa[1]]=1;
for(int i=2;i<=cl;i++)
{
if(wr[sa[i]]!=wr[sa[i-1]] || wr[sa[i]+ln]!=wr[sa[i-1]+ln]) p++;
rk[sa[i]]=p;
}
ln*=2,m=p;
}
sa[0]=0,rk[0]=0;
}

void get_h()
{
int k=0,j;
for(int i=1;i<=cl;i++) if(rk[i]!=1)
{
j=sa[rk[i]-1];
if(k) k--;
while(c[i+k]==c[j+k] && i+k<=cl && j+k<=cl) k++;
h[rk[i]]=k;
}
h[1]=0;
}

void init()
{
int i,tl;cl=0;
scanf("%s",s+1);
tl=strlen(s+1);sl=tl;
for(i=1;i<=sl;i++) c[++cl]=s[i];
scanf("%s",s+1);
tl=strlen(s+1);
c[++cl]='#';
for(i=1;i<=sl;i++) c[++cl]=s[i];
}

bool check(int x,int tmp)
{
if(tmp==0) return (x<=sl) ? 0:1;
else       return (x<=sl) ? 1:0;
}

LL solve(int tmp)
{
int tot=0;
LL sum=0,ans=0;
memset(sk,0,sizeof(sk));
memset(cnt,0,sizeof(cnt));
for(int i=1;i<=cl;i++)
{
if(h[i]<K)
{
for(int j=1;j<=tot;j++) cnt[j]=0;
tot=0;sum=0;
}
else
{
int tcnt=0,tsum=0;
while(sk[tot] > h[i]-K+1)
{
tcnt+=cnt[tot];
tsum+=cnt[tot]*sk[tot];
sk[tot]=0,cnt[tot]=0;
tot--;
}
if(tcnt)
{
sk[++tot]=h[i]-K+1;
cnt[tot]=tcnt;
sum=sum-tsum+tcnt*sk[tot];
}
if(check(sa[i],tmp)) ans+=sum;
}
if(!check(sa[i],tmp) && (cl-sa[i]+1>=K))
{
sk[++tot]=(cl-sa[i]+1)-K+1;
cnt[tot]++;
sum+=sk[tot];
}
}
return ans;
}

int main()
{
freopen("a.in","r",stdin);
freopen("me.out","w",stdout);
while(1)
{
scanf("%d",&K);
if(!K) return 0;
init();
get_sa(200);
get_h();
// for(int i=1;i<=cl;i++) printf("%d ",sa[i]);printf("\n");
// for(int i=1;i<=cl;i++) printf("%d ",rk[i]);printf("\n");
// for(int i=1;i<=cl;i++)
// {
// for(int j=sa[i];j<=cl;j++) printf("%c",c[j]);printf("\n");
// }
printf("%I64d\n",solve(0)+solve(1));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐