hihocoder 1403 1407 1415 后缀数组*3 最长公共子串/最长不重叠重复出现的子串/最长k次重复子串

hihocoder 1403:

求一个字符串中至少出现了K次的最长可重叠子串

做法:预处理出height,然后二分长度,每次用单调队列线性求区间最小值即可

#include<bits/stdc++.h>
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
using namespace std;//head
const int maxn=4e4+10,maxm=2e6+10;
int casn,n,m,k;
char a[maxn];
class suffix{public:
  int sa[maxn],rank[maxn],h[maxn],wa[maxn],wb[maxn],wc[maxn],wd[maxn];
  char *str;
  void geth(int n){
    int i,j,k=0;
    for(i=1;i<=n;++i) rank[sa[i]]=i;
    for(i=0;i<n;h[rank[i++]]=k)
    for(k?k--:0,j=sa[rank[i]-1];str[i+k]==str[j+k];++k);
  } 
  void getsa(char *_s,int n,int m){
    str=_s;
    int i,j,p,*x=wa,*y=wb;
    rep(i,0,m-1) wd[i]=0;
    rep(i,0,n-1) wd[x[i]=str[i]]++;
    rep(i,1,m-1) wd[i]+=wd[i-1];
    per(i,0,n-1) sa[--wd[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;
      rep(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j;
      rep(i,0,n-1) wc[i]=x[y[i]];
      rep(i,0,m-1) wd[i]=0;
      rep(i,0,n-1) wd[wc[i]]++;
      rep(i,1,m-1) wd[i]+=wd[i-1];
      per(i,0,n-1) sa[--wd[wc[i]]]=y[i];
      for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;++i)
      x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
    }
    geth(n-1);
  }

  int solve(int n,int k){
    deque<int>que;
    int ans=0;
    rep(i,1,n){
      while(!que.empty()&&h[i]<h[que.back()]) que.pop_back();
      que.push_back(i);
      while(!que.empty()&&i-que.front()>=k) que.pop_front();
      assert(!que.empty());
      if(!que.empty())ans=max(ans,h[que.front()]);
    }
    return ans;
  }
}suf;
int main() {
  cin>>n>>k;
  rep(i,0,n-1) {
    int x;cin>>x;
    a[i]=x;
  }
  a[n]=0;
  k--;
  suf.getsa(a,n+1,233);
  cout<<suf.solve(n,k);
  return 0;
}

 hihocoder1407:

求字符串中最长的至少不重叠地出现了2次的子串

做法:预处理出height,二分答案,每次检查每一个连续的height<mid的区间内,最大和最小的后缀距离是否>=mid即可(我换了板子)

#include<bits/stdc++.h>
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
using namespace std;//head
const int maxn=100000+10,maxm=2e6+10;
int casn,n,m,k;
int a[maxn];  
namespace suffix{
  int tr[maxn],rank[maxn],sa[maxn],h[maxn];
  int has[maxn],bir[maxn],val[maxn],deep[maxn];
  int cmp(int x,int y,int k){
    if(x+k>n||y+k>n)return 0;
    return rank[x]==rank[y]&&rank[x+k]==rank[y+k];
  }
  void getsa(int *s,int n,int m=maxn-5){
    int i,cnt;
    for(i=1;i<=n;i++)has[s[i]]++;
    for(i=1,cnt=0;i<=m;i++)if(has[i])tr[i]=++cnt;
    for(i=1;i<=m;i++)has[i]+=has[i-1];
    for(i=1;i<=n;i++)rank[i]=tr[s[i]],sa[has[s[i]]--]=i;
    for(int k=1;cnt!=n;k<<=1){
      for(i=1;i<=n;i++)has[i]=0;
      for(i=1;i<=n;i++)has[rank[i]]++;
      for(i=1;i<=n;i++)has[i]+=has[i-1];
      for(i=n;i>=1;i--)if(sa[i]>k)tr[sa[i]-k]=has[rank[sa[i]-k]]--;
      for(i=1;i<=k;i++)tr[n-i+1]=has[rank[n-i+1]]--;
      for(i=1;i<=n;i++)sa[tr[i]]=i;
      for(i=1,cnt=0;i<=n;i++)tr[sa[i]]=cmp(sa[i],sa[i-1],k) ? cnt:++cnt;
      for(i=1;i<=n;i++)rank[i]=tr[i];
    }
    for(int i=1;i<=n;i++){
      if(rank[i]==1)continue;
      for(int j=max(1,h[rank[i-1]]-1);;j++){
        if(s[i+j-1]==s[sa[rank[i]-1]+j-1])h[rank[i]]=j;
        else break;
      }
    }
  }
  bool check(int mid,int n){
    int mx=0,mn=n;
    rep(i,1,n){
      if(h[i]<mid){
        if(mx&&mx-mn>=mid) return true;
        mx=mn=sa[i];
      }else mx=max(mx,sa[i]),mn=min(mn,sa[i]);
    }
    if(mx&&mx-mn>=mid) return true;
    return false;
  }
}
int main() {
  cin>>n;
  rep(i,1,n) cin>>a[i];
  suffix::getsa(a,n,1010);
  int l=0,r=n;
  int ans=0;
  while(l<=r){
    int mid=(l+r)>>1;
    if(suffix::check(mid,n)) ans=mid,l=mid+1;
    else r=mid-1;
  }
  cout<<ans;
  return 0;
}

 hihocoder 1415

求两个子串的最长公共子串

做法:拼接两个子串,中间隔着一个从没出现过的字符(我选择的是比所有字符都大的一个字符),

然后计算出height数组,接着对于每一对相邻的后缀,如果分别属于两个字符串,那么ans和他们两个的lcp取一下最大值就行

#include<bits/stdc++.h>
#define rep(ii,a,b) for(int ii=a;ii<=b;++ii)
#define per(ii,a,b) for(int ii=b;ii>=a;--ii)
using namespace std;//head
const int maxn=100000+10,maxm=2e6+10;
int casn,k;
namespace suffix{
  int tr[maxn],rank[maxn],sa[maxn],h[maxn];
  int has[maxn],bir[maxn],val[maxn],deep[maxn];
  int n;
  int cmp(int x,int y,int k){
    if(x+k>n||y+k>n)return 0;
    return rank[x]==rank[y]&&rank[x+k]==rank[y+k];
  }
  void getsa(int *s,int _n,int m=maxn-5){
    int i,cnt;n=_n;
    for(i=1;i<=n;i++)has[s[i]]++;
    for(i=1,cnt=0;i<=m;i++)if(has[i])tr[i]=++cnt;
    for(i=1;i<=m;i++)has[i]+=has[i-1];
    for(i=1;i<=n;i++)rank[i]=tr[s[i]],sa[has[s[i]]--]=i;
    for(int k=1;cnt!=n;k<<=1){
      for(i=1;i<=n;i++)has[i]=0;
      for(i=1;i<=n;i++)has[rank[i]]++;
      for(i=1;i<=n;i++)has[i]+=has[i-1];
      for(i=n;i>=1;i--)if(sa[i]>k)tr[sa[i]-k]=has[rank[sa[i]-k]]--;
      for(i=1;i<=k;i++)tr[n-i+1]=has[rank[n-i+1]]--;
      for(i=1;i<=n;i++)sa[tr[i]]=i;
      for(i=1,cnt=0;i<=n;i++)tr[sa[i]]=cmp(sa[i],sa[i-1],k) ? cnt:++cnt;
      for(i=1;i<=n;i++)rank[i]=tr[i];
    }
    for(int i=1;i<=n;i++){
      if(rank[i]==1)continue;
      for(int j=max(1,h[rank[i-1]]-1);;j++){
        if(s[i+j-1]==s[sa[rank[i]-1]+j-1])h[rank[i]]=j;
        else break;
      }
    }
  }
  int solve(int len,int n){
    int ans=0;
    rep(i,1,len) if((sa[i]<=n)^(sa[i-1]<=n))
        ans=max(ans,h[i]);
    return ans;
  }
}
char a[maxn],b[maxn];
int c[maxn<<1];
int main() {
  cin>>(a+1)>>(b+1);
  int n=strlen(a+1),m=strlen(b+1);
  rep(i,1,n) c[i]=a[i];c[n+1]='z'+1;
  rep(i,1,m) c[i+n+1]=b[i];
  int len=n+m+1;
  suffix::getsa(c,len,233);
  cout<<suffix::solve(len,n);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/nervendnig/p/11437746.html
今日推荐