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\n",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\n",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\n",j,s1[i+1],p);
    else ans=min(ans,f[i][j]+1);
      }
  if(ans==inf)ans=-1; printf("%d\n",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\n",ans);
  return 0;
}

 

posted @ 2018-12-12 14:20  Zinn  阅读(189)  评论(0编辑  收藏  举报