zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    CSS 会被继承的属性
    List的遍历和删除元素
    java中unicode和中文相互转换
    Hibernate注解方式一对多自关联关系映射
    HQL: Hibernate查询语言
    java replaceall 使用正则表达式替换单等号,不替换其他相关的等号。
    Java 将图片转二进制再将二进制转成图片
    返回上一页并刷新与返回不刷新代码
    Css圆角边框
    jquery mobile
  • 原文地址:https://www.cnblogs.com/nervendnig/p/11437746.html
Copyright © 2011-2022 走看看