zoukankan      html  css  js  c++  java
  • bzoj 3796 Mushroom追妹纸——后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3796

    长度一般都是 1e5 ,看这个是 5e4 ,一看就是把两个串接起来做。

    自己本来想的是把 s3 分别接到 s1 和 s2 后面,做后缀数组求出 s1 和 s2 的每个位置有没有作为开头出现了 s3 ;然后把 s1 和 s2 接起来做后缀数组,二分一个长度作为答案,按 sa[ ] 的顺序遍历每个位置,遇到 s2 的就记录一下它能不能让之后的 s1 和某个 s2 的 LCP >= ans_len ,遇到 s1 就看有没有 s2 和它的 LCP >= ans_len (O(1)),有的话再看看 s1 对应的那个位置后面 ans_len 区域内有没有完整地出现 s3 (可以给每个位置记录 lst[ ] 表示最近的下一个 s3 开头出现的位置),如果没有的话,这个 ans_len 可行;还要倒着做一遍统计后面的 s2 对前面的 s1 的影响。

    看了一下题解。原来不用二分答案,因为可以取尽量大的更新答案;而且对于一个 s1 ,最好的 s2 一定是离它最近的 s2 ,因为 LCP 越远越短了;找到一个 LCP 之后,就算后面对应区域内出现了完整的 s3 也不用认为这个 LCP 不合法,可以让 LCP 和不含 s3 部分的长度取 min 来更新答案。

    而且算 s1 和 s2 的每个位置有没有作为开头出现 s3 也可以不用后缀数组,做 kmp 即可。

    正着和倒着做也可以等价为正着同时统计 s2 对 s1 的影响和 s1 对 s2 的影响。大概是记录 mn1 和 mn2 分别表示 s1 能给出多少贡献和 s2 能给出多少贡献,那么遇到一个 s1 的位置,可以让 mn1 = INF , mn2 = min( mn2 , ht[ i ] ) ;因为 i 位置能否贡献是看从 i+1 开始的 ht[ ] 取 min 的。

    注意数组大小。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N=5e4+5,M=1e5+5,M2=1e4+5,K=20;
    int sa[M],rk[M],tp[M],tx[M],ht[M],lst[M],nxt[M+M2];
    char s[M+M2],s1[N],s2[N],s3[N];
    int Mn(int a,int b){return a<b?a:b;}
    int Mx(int a,int b){return a>b?a:b;}
    void kmp(int m3,int m1,int m2)
    {
      int n=m1+m2+m3+2;
      for(int i=2;i<=n;i++)
        {
          int cr=nxt[i-1];
          while(cr&&s[cr+1]!=s[i])cr=nxt[cr];
          if(s[cr+1]==s[i])nxt[i]=cr+1;
          if(nxt[i]==m3)
        lst[i-m3-1-m3+1]=1;
        }
      lst[m1+m2+2]=m1+m2+2;
      for(int i=m1+m2+1;i;i--)
        lst[i]=(lst[i]?i:lst[i+1]);
    }
    void Rsort(int n,int nm)
    {
      for(int i=1;i<=nm;i++)tx[i]=0;
      for(int i=1;i<=n;i++)tx[rk[i]]++;
      for(int i=2;i<=nm;i++)tx[i]+=tx[i-1];
      for(int i=n;i;i--)sa[tx[rk[tp[i]]]--]=tp[i];
    }
    void get_sa(int n)
    {
      int nm=27;
      for(int i=1;i<=n;i++)tp[i]=i,rk[i]=s[i]-'a'+1;
      Rsort(n,nm);
      for(int k=1;k<=n;k<<=1)
        {
          int tot=0;
          for(int i=n-k+1;i<=n;i++)tp[++tot]=i;
          for(int i=1;i<=n;i++)
        if(sa[i]>k)tp[++tot]=sa[i]-k;
          Rsort(n,nm);memcpy(tp,rk,sizeof rk);
          nm=1;rk[sa[1]]=1;
          for(int i=2,u,v;i<=n;i++)
        {
          u=sa[i]+k;v=sa[i-1]+k;if(u>n)u=0;if(v>n)v=0;
          rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[u]==tp[v])?nm:++nm;
        }
          if(nm==n)break;
        }
    }
    void get_ht(int n)
    {
      for(int i=1,k=0,j;i<=n;i++)
        {
          for(k?k--:0,j=sa[rk[i]-1];i+k<=n&&j+k<=n&&s[i+k]==s[j+k];k++);
          ht[rk[i]]=k;//rk[i]!!
        }
    }
    void calc(int ps,int len,int &ans,int m3)
    {
      int ret=Mn(len,lst[ps]-ps+m3-1);
      ans=Mx(ans,ret);
    }
    int main()
    {
      scanf("%s",s1+1);scanf("%s",s2+1);scanf("%s",s3+1);
      int m1=strlen(s1+1),m2=strlen(s2+1),m3=strlen(s3+1),n=m1+m2+1;
      memcpy(s,s3,sizeof s3);s[m3+1]=',';
      for(int i=m3+2,j=1;j<=m1;j++,i++)s[i]=s1[j];
      s[m3+1+m1+1]='!';//don't be the same with s[m3+1]
      for(int i=m3+1+m1+1+1,j=1;j<=m2;j++,i++)s[i]=s2[j];
      kmp(m3,m1,m2);
      memcpy(s,s1,sizeof s1);s[m1+1]='z'+1;
      for(int i=m1+2,j=1;j<=m2;j++,i++)s[i]=s2[j];
      get_sa(n);get_ht(n); int ans=0;
      for(int i=1,mn1=0,mn2=0;i<=n;i++)
        {
          if(sa[i]<=m1)
        {
          mn1=n; mn2=Mn(mn2,ht[i]);
          calc(sa[i],mn2,ans,m3);
        }
          else if(sa[i]>m1+1)
        {
          mn2=n; mn1=Mn(mn1,ht[i]);
          calc(sa[i],mn1,ans,m3);
        }
        }
      printf("%d
    ",ans);
      return 0;
    }
  • 相关阅读:
    【连载】Bootstrap开发漂亮的前端界面之插件开发
    【连载】Bootstrap开发漂亮的前端界面之自定义右键菜单
    Bootstrap开发漂亮的前端界面之实现原理
    终于有SpringMvc与Struts2的对比啦
    【G】开源的分布式部署解决方案文档
    【G】开源的分布式部署解决方案文档
    【G】开源的分布式部署解决方案文档
    【G】开源的分布式部署解决方案文档
    【轮子狂魔】手把手教你用JS给博客动态增加目录
    【G】系列导航
  • 原文地址:https://www.cnblogs.com/Narh/p/10085846.html
Copyright © 2011-2022 走看看