zoukankan      html  css  js  c++  java
  • bzoj 4032 [ HEOI 2015 ] 最短不公共子串 —— 后缀自动机+序列自动机

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

    序列自动机其实就是每个位置记录一下某字母后面第一个出现位置,为了子序列能尽量长。

    对字符串B建一个后缀自动机,一个序列自动机,然后让A在上面找即可;

    1.枚举A每个位置开始的子串,在SAM上走,失配就找到了;

    2.枚举A子串,用B的序列自动机找失配处;

    3.设 f[i][j] 表示A串前 i 个字符形成的子序列对应SAM上节点 j 时的最短子序列长度,可以转移;

    4.f[i][j] 表示A串前 i 个字符的子序列对应B串第 j 个位置时的最短子序列长度,用序列自动机转移;

    所以注意 f[i][j] 的第二维要开 4000!否则不是 MLE 而是 WA 。

    代码如下:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int const xn=2005,inf=0x3f3f3f3f;
    int fa[xn<<1],l[xn<<1],go[xn<<1][30],lst=1,cnt=1,f[xn][xn<<1],g[xn][30],nxt[30];//<<1!!
    char s1[xn],s2[xn];
    void add(int w)
    {
      int p=lst,np=++cnt; lst=np; l[np]=l[p]+1;
      for(;p&&!go[p][w];p=fa[p])go[p][w]=np;
      if(!p)fa[np]=1;
      else
        {
          int q=go[p][w];
          if(l[q]==l[p]+1)fa[np]=q;
          else
        {
          int nq=++cnt; l[nq]=l[p]+1;
          memcpy(go[nq],go[q],sizeof go[q]);
          fa[nq]=fa[q]; fa[q]=fa[np]=nq;
          for(;go[p][w]==q;p=fa[p])go[p][w]=nq;
        }
        }
    }
    int main()
    {
      scanf("%s",s1+1); int l1=strlen(s1+1);
      scanf("%s",s2+1); int l2=strlen(s2+1);
      for(int i=1;i<=l1;i++)s1[i]-='a';
      for(int i=1;i<=l2;i++)s2[i]-='a';
      for(int i=1;i<=l2;i++)add(s2[i]);
      for(int i=0;i<26;i++)nxt[i]=l2+1;
      for(int i=l2;i>=0;i--)
        {
          for(int j=0;j<26;j++)g[i][j]=nxt[j];
          nxt[s2[i]]=i;
        }
      
      int ans=inf;
      for(int i=1;i<=l1;i++)
        {
          int p=1,j;
          for(j=i;j<=l1;j++)
        {
          if(!go[p][s1[j]])break;
          p=go[p][s1[j]];
        }
          if(j<=l1)ans=min(ans,j-i+1);
        }
      if(ans==inf)ans=-1; printf("%d
    ",ans);
      
      ans=inf;
      for(int i=1;i<=l1;i++)
        {
          if(g[0][s1[i]]>l2){ans=min(ans,1); continue;}
          int p=g[0][s1[i]],j;
          for(j=i+1;j<=l1;j++)
        {
          if(g[p][s1[j]]==l2+1)break;
          p=g[p][s1[j]];
        }
          if(j<=l1)ans=min(ans,j-i+1);
        }
      if(ans==inf)ans=-1; printf("%d
    ",ans);
    
      memset(f,0x3f,sizeof f); f[0][1]=0; ans=inf;
      for(int i=0;i<l1;i++)
        for(int j=1,p;j<=cnt;j++)
          {
        if(f[i][j]==inf)continue;
        f[i+1][j]=min(f[i+1][j],f[i][j]);
        if((p=go[j][s1[i+1]]))f[i+1][p]=min(f[i+1][p],f[i][j]+1);//,printf("%d %d p=%d
    ",j,s1[i+1],p);
        else ans=min(ans,f[i][j]+1);
          }
      if(ans==inf)ans=-1; printf("%d
    ",ans);
                    
      memset(f,0x3f,sizeof f); f[0][0]=0; ans=inf;
      for(int i=0;i<l1;i++)
        for(int j=0,p;j<=l2;j++)
          {
        if(f[i][j]==inf)continue;
        f[i+1][j]=min(f[i+1][j],f[i][j]);
        if((p=g[j][s1[i+1]])<l2+1)f[i+1][p]=min(f[i+1][p],f[i][j]+1);
        else ans=min(ans,f[i][j]+1);
          }
      if(ans==inf)ans=-1; printf("%d
    ",ans);
      return 0;
    }
  • 相关阅读:
    6-4.粗体标签
    [Unity3D] 如何实现点击按钮退出游戏
    [Unity3D] 载入游戏地图时背景图片随机切换 & 数字百分比进度条
    [Unity3D] 鼠标点击图片移动效果
    [3DMAX]如何将骨骼与模型绑定在一起(蒙皮) & 如何实现自动化人物模型蒙皮
    [Unity 3D]用鼠标滚轮实现镜头放大和缩放,并添加距离限制
    [Unity3D] 如何实现围绕旋转
    [Unity3D] 如何实现注视旋转
    Css 图片自适应
    Scss 定义内层class的简单写法
  • 原文地址:https://www.cnblogs.com/Zinn/p/10108092.html
Copyright © 2011-2022 走看看