• BZOJ4032:[HEOI2015]最短不公共子串(SAM)


    Description

     在虐各种最长公共子串、子序列的题虐的不耐烦了之后,你决定反其道而行之。

    一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。
    一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。
    下面,给两个小写字母串A,B,请你计算:
    (1) A的一个最短的子串,它不是B的子串
    (2) A的一个最短的子串,它不是B的子序列
    (3) A的一个最短的子序列,它不是B的子串
    (4) A的一个最短的子序列,它不是B的子序列

    Input

    有两行,每行一个小写字母组成的字符串,分别代表A和B。

    Output

    输出4行,每行一个整数,表示以上4个问题的答案的长度。如果没有符合要求的答案,输出-1.

    Sample Input

    aabbcc
    abcabc

    Sample Output

    2
    4
    2
    4

    HINT

     对于100%的数据,A和B的长度都不超过2000

    Solution

    强行四合一?

    (一)枚举$A$串的左端点,然后从左端点开始往后在$B$串的$SAM$上面跑,一旦失配就更新答案然后$break$

    (二)预处理数组$next[i][j]$表示从$i$后面的第一次出现字母$j$的位置。预处理出$nextA$和$nextB$,然后枚举$A$左端点往后贪心,如果失配就更新答案然后$break$

    (三)设$len[i]$表示在$B$串的$SAM$的$i$点的时候最短的长度。然后用$A$串在$B$的$SAM$上面跑。如果失配就更新答案,否则就更新$len$。

    (四)设$len[i]$表示在$B$串的$i$位置的时候最短的长度。然后用$A$串的每一个字母,借$next$数组倒序去更新$len$。如果失配就更新答案,否则就更新$len$。至于为什么要倒序,其实是和背包差不多的原理,并不难想。

    Code

      1 #include<iostream>
      2 #include<cstring>
      3 #include<cstdio>
      4 #define N (4009)
      5 using namespace std;
      6 
      7 char s[N],t[N];
      8 int slen,tlen,nextA[N][28],nextB[N][28],last[28],len[N];
      9 
     10 struct SAM
     11 {
     12     int son[N][28],fa[N],step[N],wt[N],od[N];
     13     int p,q,np,nq,last,cnt;
     14     SAM(){last=cnt=1;}
     15 
     16     void Insert(int x)
     17     {
     18         p=last; np=last=++cnt; step[np]=step[p]+1;
     19         while (p && !son[p][x]) son[p][x]=np, p=fa[p];
     20         if (!p) fa[np]=1;
     21         else
     22         {
     23             q=son[p][x];
     24             if (step[q]==step[p+1]) fa[np]=q;
     25             else
     26             {
     27                 nq=++cnt; step[nq]=step[p]+1;
     28                 memcpy(son[nq],son[q],sizeof(son[q]));
     29                 fa[nq]=fa[q]; fa[np]=fa[q]=nq;
     30                 while (son[p][x]==q) son[p][x]=nq, p=fa[p];
     31             }
     32         }
     33     }
     34 }SAM;
     35 
     36 void CalcNext()
     37 {
     38     memset(last,-1,sizeof(last));
     39     for (int i=slen; i>=0; --i)
     40     {
     41         for (int j=0; j<26; ++j) nextA[i][j]=last[j];
     42         last[s[i]-'a']=i;
     43     }
     44     memset(last,-1,sizeof(last));
     45     for (int i=tlen; i>=0; --i)
     46     {
     47         for (int j=0; j<26; ++j) nextB[i][j]=last[j];
     48         last[t[i]-'a']=i;
     49     }
     50 }
     51 
     52 void Sub1()
     53 {
     54     int ans=2e9;
     55     for (int i=1; i<=slen; ++i)
     56     {
     57         int now=1;
     58         for (int j=i; j<=slen; ++j)
     59         {
     60             if (!SAM.son[now][s[j]-'a']) {ans=min(ans,j-i+1); break;}
     61             now=SAM.son[now][s[j]-'a'];
     62         }
     63     }
     64     printf("%d
    ",ans==2e9?-1:ans);
     65 }
     66 
     67 void Sub2()
     68 {
     69     int ans=2e9;
     70     for (int i=1; i<=slen; ++i)
     71     {
     72         int now=0;
     73         for (int j=i; j<=slen; ++j)
     74         {
     75             if (nextB[now][s[j]-'a']==-1) {ans=min(ans,j-i+1); break;}
     76             now=nextB[now][s[j]-'a'];
     77         }
     78     }
     79     printf("%d
    ",ans==2e9?-1:ans);
     80 }
     81 
     82 void Sub3()
     83 {
     84     int ans=2e9;
     85     memset(len,0x7f,sizeof(len));
     86     len[1]=1;
     87     for (int i=1; i<=slen; ++i)
     88         for (int j=1; j<=SAM.cnt; ++j)
     89         {
     90             int nxt=SAM.son[j][s[i]-'a'];
     91             if (!nxt) ans=min(ans,len[j]);
     92             else len[nxt]=min(len[nxt],len[j]+1);
     93         }
     94     printf("%d
    ",ans==2e9?-1:ans);
     95 }
     96 
     97 void Sub4()
     98 {
     99     int ans=2e9;
    100     memset(len,0x7f,sizeof(len));
    101     len[0]=0;
    102     for (int i=1; i<=slen; ++i)
    103         for (int j=tlen; j>=0; --j)
    104         {
    105             int nxt=nextB[j][s[i]-'a'];
    106             if (nxt==-1) ans=min(ans,len[j]+1);
    107             else len[nxt]=min(len[nxt],len[j]+1);
    108         }
    109     printf("%d
    ",ans==2e9?-1:ans);
    110 }
    111 
    112 int main()
    113 {
    114     scanf("%s%s",s+1,t+1);
    115     slen=strlen(s+1), tlen=strlen(t+1);
    116     for (int i=1; i<=tlen; ++i)
    117         SAM.Insert(t[i]-'a');
    118     CalcNext();
    119     Sub1(); Sub2(); Sub3(); Sub4();
    120 }
  • 相关阅读:
    第二个月课堂004讲解python之实战之元组(003)_高级讲师肖sir
    第二个月课堂004讲解python之实战之列表(002)_高级讲师肖sir
    多测师课堂_mysql之报错日志(001)高级讲师肖sir
    多测师课堂012_mysql之存储过程(练习和答案)高级讲师肖sir
    linux alias 命令 查看系统设置的命令别名
    前端 CSS 介绍
    前端 CSS语法
    前端 CSS 注释
    前端 CSS 目录
    linux echo 命令 打印字符串
  • 原文地址:https://www.cnblogs.com/refun/p/10013106.html
Copyright © 2020-2023  润新知