NKOJ 4022(HEOI 2015)最短不公共子串(后缀自动机+序列自动机+dp)

P4022 [HEOI2015]最短不公共子串

问题描述

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

一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是。

一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是。

下面,给两个小写字母串A,B,请你计算:

(1) A的一个最短的子串,它不是B的子串

(2) A的一个最短的子串,它不是B的子序列

(3) A的一个最短的子序列,它不是B的子串

(4) A的一个最短的子序列,它不是B的子序列

输入格式

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

输出格式

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

样例输入

aabbcc
abcabc

样例输出

2
4
2
4

提示

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


此题涉及到子串和子序列,考虑后缀自动机和序列自动机。
那么此题就是在两个自动机上dp。
对于第一问,令 F1[x][y] F 1 [ x ] [ y ] 表示从A的后缀自动机上x节点,B的后缀自动机上y节点出发的最短不公共子串长度,那么当 x0,y=0 x ≠ 0 , y = 0 F1[x][y]=0 F 1 [ x ] [ y ] = 0 ,然后直接在两个自动机上转移就行了, F1[x][y]=min(F1[tx][ty]+1) F 1 [ x ] [ y ] = m i n ( F 1 [ t x ] [ t y ] + 1 )
对于第二问,在A的后缀自动机和B的序列自动机上同样dp即可。
对于第三问,在A的序列自动机和B的后缀自动机上同样dp即可。
对于第四问,在A的序列自动机和B的序列自动机上同样dp即可。

复杂度 O(26n2)? O ( 26 n 2 ) ?


代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#define N 4005
using namespace std;
struct PAM
{
    int tot,rt,las[26],pre[N],son[N][26];
    PAM()
    {
        tot=rt=1;
        for(int i=0;i<26;i++)las[i]=1;
    }
    void Ins(int c)
    {
        tot++;pre[tot]=las[c];
        for(int i=0;i<26;i++)
        for(int j=las[i];j&&!son[j][c];j=pre[j])son[j][c]=tot;
        las[c]=tot;
    }
}PA,PB;
struct SAM
{
    int rt,tot,las,son[N][26],Max[N],pra[N];
    SAM(){tot=las=rt=1;}
    int NP(int x)
    {
        Max[++tot]=x;
        return tot;
    }
    void Ins(int t)
    {
        int p=las,q,np,nq;
        np=NP(Max[p]+1);
        while(p&&!son[p][t])son[p][t]=np,p=pra[p];
        if(!p)pra[np]=rt;
        else
        {
            q=son[p][t];
            if(Max[q]==Max[p]+1)pra[np]=q;
            else
            {
                nq=NP(Max[p]+1);
                memcpy(son[nq],son[q],sizeof(son[q]));
                pra[nq]=pra[q];
                pra[q]=pra[np]=nq;
                while(son[p][t]==q)son[p][t]=nq,p=pra[p];
            }
        }
        las=np;
    }
}SA,SB;
char s1[N],s2[N];
int n,m,ans;
int F1[N][N],F2[N][N>>1],F3[N>>1][N],F4[N>>1][N>>1];
int DFS1(int x,int y)
{
    if(x&&y==0)return 0;
    if(!x||!y)return 1e9;
    if(F1[x][y]!=-1)return F1[x][y];
    F1[x][y]=1e9;
    for(int i=0;i<26;i++)F1[x][y]=min(F1[x][y],DFS1(SA.son[x][i],SB.son[y][i])+1);
    return F1[x][y];
}
int DFS2(int x,int y)
{
    if(x&&y==0)return 0;
    if(!x||!y)return 1e9;
    if(F2[x][y]!=-1)return F2[x][y];
    F2[x][y]=1e9;
    for(int i=0;i<26;i++)F2[x][y]=min(F2[x][y],DFS2(SA.son[x][i],PB.son[y][i])+1);
    return F2[x][y];
}
int DFS3(int x,int y)
{
    if(x&&y==0)return 0;
    if(!x||!y)return 1e9;
    if(F3[x][y]!=-1)return F3[x][y];
    F3[x][y]=1e9;
    for(int i=0;i<26;i++)F3[x][y]=min(F3[x][y],DFS3(PA.son[x][i],SB.son[y][i])+1);
    return F3[x][y];
}
int DFS4(int x,int y)
{
    if(x&&y==0)return 0;
    if(!x||!y)return 1e9;
    if(F4[x][y]!=-1)return F4[x][y];
    F4[x][y]=1e9;
    for(int i=0;i<26;i++)F4[x][y]=min(F4[x][y],DFS4(PA.son[x][i],PB.son[y][i])+1);
    return F4[x][y];
}
int main()
{
    scanf("%s%s",&s1[1],&s2[1]);
    s1[0]=s2[0]='%';
    n=strlen(s1)-1;
    m=strlen(s2)-1;
    for(int i=1;i<=n;i++)PA.Ins(s1[i]-'a'),SA.Ins(s1[i]-'a');
    for(int i=1;i<=m;i++)PB.Ins(s2[i]-'a'),SB.Ins(s2[i]-'a');
    memset(F1,-1,sizeof(F1));
    memset(F2,-1,sizeof(F2));
    memset(F3,-1,sizeof(F3));
    memset(F4,-1,sizeof(F4));
    ans=DFS1(1,1);printf("%d\n",ans==1e9?-1:ans);
    ans=DFS2(1,1);printf("%d\n",ans==1e9?-1:ans);
    ans=DFS3(1,1);printf("%d\n",ans==1e9?-1:ans);
    ans=DFS4(1,1);printf("%d\n",ans==1e9?-1:ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值