BZOJ4340 : BJOI2015 隐身术

枚举$B$串的每个后缀,统计出该后缀所有满足条件的前缀。

考虑暴力搜索,设状态$(x,y,z)$表示当前需要考虑$A$从$x$开始的后缀,$B$从$y$开始的后缀,之前部分编辑距离为$z$。

那么首先用后缀数组+ST表求出两个后缀的lcp,$x$和$y$都可以向右跳那么多,且不产生任何代价。

如果此时匹配到了底,那么可以得到在一段区间$[L,R]$内,所有前缀都是合法的。注意到这种前缀只有$2K+1$种,所以可以使用差分前缀和来进行标记。

如果还没有匹配到底,那么有$3$种选择,分别是状态$(x+1,y,z+1)$、$(x,y+1,z+1)$以及$(x+1,y+1,z+1)$。

时间复杂度$O(nlog n+n3^k)$。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
char s[N],A[N],B[N];
int K,na,nb,n,m,i,j,rk[N],sa[N],height[N],tmp[N],cnt[N],Log[N],f[17][N],c[15],ans;
void suffixarray(int n,int m){
  int i,j,k;n++;
  for(i=0;i<n;i++)cnt[rk[i]=s[i]]++;
  for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
  for(i=0;i<n;i++)sa[--cnt[rk[i]]]=i;
  for(k=1;k<=n;k<<=1){
    for(i=0;i<n;i++){
      j=sa[i]-k;
      if(j<0)j+=n;
      tmp[cnt[rk[j]]++]=j;
    }
    sa[tmp[cnt[0]=0]]=j=0;
    for(i=1;i<n;i++){
      if(rk[tmp[i]]!=rk[tmp[i-1]]||rk[tmp[i]+k]!=rk[tmp[i-1]+k])cnt[++j]=i;
      sa[tmp[i]]=j;
    }
    memcpy(rk,sa,n*sizeof(int));
    memcpy(sa,tmp,n*sizeof(int));
    if(j>=n-1)break;
  }
  for(j=rk[height[i=k=0]=0];i<n-1;i++,k++)
    while(~k&&s[i]!=s[sa[j-1]+k])height[j]=k--,j=rk[sa[j]+1];
}
inline int ask(int x,int y){
  int k=Log[y-x+1];
  return min(f[k][x],f[k][y-(1<<k)+1]);
}
inline int lcp(int x,int y){
  if(x>=na||y>=nb)return 0;
  x=rk[x],y=rk[y+na+1];
  if(x>y)swap(x,y);
  return ask(x+1,y);
}
inline void col(int l,int r){
  if(l<i)l=i;
  if(r>=nb)r=nb-1;
  c[l-i-na+K+2]++,c[r-i-na+K+3]--;
}
void dfs(int x,int y,int z){
  int t=lcp(x,y);
  x+=t,y+=t;
  if(x==na||y==nb){
    int c=K-(z+na-x);
    if(c>=0)col(y-1-c,y-1+c);
    return;
  }
  if(z==K)return;
  z++;
  dfs(x+1,y,z);
  dfs(x,y+1,z);
  dfs(x+1,y+1,z);
}
int main(){
  scanf("%d%s%s",&K,A,B);
  na=strlen(A),nb=strlen(B);
  for(i=0;i<na;i++)s[n++]=A[i];
  s[n++]='#';
  for(i=0;i<nb;i++)s[n++]=B[i];
  suffixarray(n,128);
  for(i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
  for(i=1;i<=n;i++)f[0][i]=height[i];
  for(j=1;j<17;j++)for(i=1;i+(1<<j-1)<=n;i++)f[j][i]=min(f[j-1][i],f[j-1][i+(1<<j-1)]);
  for(m=2*K+1,i=0;i<nb;i++){
    for(j=1;j<=m;j++)c[j]=0;
    dfs(0,i,0);
    for(j=1;j<=m;j++)if(c[j]+=c[j-1])ans++;
  }
  return printf("%d",ans),0;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/5106772.html