【BZOJ4032】【HEOI2015】最短不公共子串 后缀自动机

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/45313429");
}

题解:

T1:

我们按长度bfs所有的串,对于每个串记录A串中终点位置、B串中终点位置(B串中位置由B的后缀自动机中节点标号表示)、长度——(x,y,l)。

然后 (x,y,l) 可以 O(1) 转移到 (x+1,son[y,stringax+1?′a′],l+1)

时间复杂度 O(n2)

T2:

我们记录prex,i 表示 位置 x 之后第一个字母 i 的位置。

然后 (x,y,l) 可以 O(1) 转移到 (x+1,pre[y,stringax+1?′a′],l+1)

时间复杂度 O(n2)

T3:

我们发现对于有相同 (x,y) 的 (x,y,l) 只取一个 l 最小的就好啦,因为对于 l 不同的 (x,y) ,其后需要的不公共子串的最短达成 Δl 是相同的!

枚举子序列的方法是每个节点都可以枚举它后面还有哪些字母,

然后 (x,y,l) 可以 O(1) 转移到 (pre[x,?],son[y,?],l+1)

所以可以判断去重。

时间复杂度 O(n2) 有 26 的常数。

T4:

把前面的略结合一下就好啦~

然后 (x,y,l) 可以 O(1) 转移到 (pre[x,?],preB[y,?],l+1)

时间复杂度 O(n2) 有 26 的常数。

-

另外前两问也可以用判断去重的方法。

另外要注意去重的这个东西需要开 [N][N<<1]

代码:

真是悲伤的故事……

我优化了一下我的常数,结果它慢了将近一半……

400ms → 552ms

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2020
#define T 26
using namespace std;
struct SAM
{
    int son[N<<1][T],dep[N<<1],pa[N<<1],last,cnt;
    void init(){last=cnt=1;}
    int newnode(int p){dep[++cnt]=dep[p]+1;return cnt;}
    void add(int x)
    {
        int p=last;
        int np=newnode(p);
        while(p&&son[p][x]==0)son[p][x]=np,p=pa[p];
        if(!p)pa[np]=1;
        else {
            int q=son[p][x];
            if(dep[q]==dep[p]+1)pa[np]=q;
            else {
                int nq=newnode(p);
                pa[nq]=pa[q],pa[q]=pa[np]=nq;
                memcpy(son[nq],son[q],sizeof son[q]);
                while(p&&son[p][x]==q)son[p][x]=nq,p=pa[p];
            }
        }
        last=np;
    }
    void print()
    {
        for(int i=1;i<=30;i++)
        {
            for(int j=0;j<T;j++)if(son[i][j])
                printf("%d %d\n",i,son[i][j]);
        }
    }
}sam;
char s[N],t[N];
int pre1[N][T],pre2[N][T],n,m;
struct Eli
{
    int x,y,z;
    Eli(int _x=0,int _y=0,int _z=0):x(_x),y(_y),z(_z){}
};
int vis[N][N<<1];
queue<Eli>q;
int getans(int f)
{
    int i,x,y;
    Eli U;
    while(!q.empty())q.pop();
    if(f<3)
    {
        U.z=0;
        if(f==1)U.y=1;
        else U.y=0;
        for(i=0;i<n;i++)
        {
            U.x=i;
            q.push(U);
        }
        while(!q.empty())
        {
            U=q.front(),q.pop();
            x=U.x+1,y=s[x]-‘a‘;
            if(x>n)continue;
            if(f==1)y=sam.son[U.y][y];
            else y=pre2[U.y][y];

            if(!y)return U.z+1;
            else if(vis[x][y]!=f)
            {
                vis[x][y]=f;
                q.push(Eli(x,y,U.z+1));
            }
        }
    }
    else if(f==3)
    {
        q.push(Eli(0,1,0));
        while(!q.empty())
        {
            U=q.front(),q.pop();
            for(i=0;i<T;i++)if(x=pre1[U.x][i])
            {
                y=sam.son[U.y][i];
                if(!y)return U.z+1;
                else if(vis[x][y]!=f)
                {
                    vis[x][y]=f;
                    q.push(Eli(x,y,U.z+1));
                }
            }
        }
    }
    else {
        q.push(Eli(0,0,0));
        while(!q.empty())
        {
            U=q.front(),q.pop();
            for(i=0;i<T;i++)if(x=pre1[U.x][i])
            {
                y=pre2[U.y][i];
                if(!y)return U.z+1;
                else if(vis[x][y]!=f)
                {
                    vis[x][y]=f;
                    q.push(Eli(x,y,U.z+1));
                }
            }
        }
    }
    return -1;
}
int main()
{
    int i,j,k;
    scanf("%s%s",s+1,t+1);
    n=strlen(s+1),m=strlen(t+1);
    sam.init();
    for(i=1;i<=m;i++)sam.add(t[i]-‘a‘);
    for(i=n-1;~i;i--)
    {
        memcpy(pre1[i],pre1[i+1],sizeof pre1[i+1]);
        pre1[i][s[i+1]-‘a‘]=i+1;
    }
    for(i=m-1;~i;i--)
    {
        memcpy(pre2[i],pre2[i+1],sizeof pre2[i+1]);
        pre2[i][t[i+1]-‘a‘]=i+1;
    }
    printf("%d\n%d\n%d\n%d\n",getans(1),getans(2),getans(3),getans(4));
    return 0;
}
时间: 2024-10-18 14:52:09

【BZOJ4032】【HEOI2015】最短不公共子串 后缀自动机的相关文章

BZOJ 4032 HEOI2015 最短不公共子串 后缀自动机+序列自动机+BFS

题目大意:给定字符串A和B,求A最短的子串/子序列S满足S不是B的子串/子序列 这题真TM有毒*2 搞法类似这道题 然后子串是后缀自动机 子序列自然就是序列自动机了= = 每更新一个x节点时所有没有x的后继的节点都连向这个节点 每个节点的parent是这个字母上一次出现的位置 每个字母记录最后一次出现的位置 更新指针时沿着parent指针撸一遍就行了 #include <cstdio> #include <cstring> #include <iostream> #in

bzoj4032: [HEOI2015]最短不公共子串

Description 在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之. 一个串的“子串”指的是它的连续的一段,例如bcd是abcdef的子串,但bde不是. 一个串的“子序列”指的是它的可以不连续的一段,例如bde是abcdef的子串,但bdd不是. 下面,给两个小写字母串A,B,请你计算: (1) A的一个最短的子串,它不是B的子串 (2) A的一个最短的子串,它不是B的子序列 (3) A的一个最短的子序列,它不是B的子串 (4) A的一个最短的子序列,它不是B的子序列

【BZOJ】4032: [HEOI2015]最短不公共子串(LibreOJ #2123)

[题意]给两个小写字母串A,B,请你计算: (1) A的一个最短的子串,它不是B的子串 (2) A的一个最短的子串,它不是B的子序列 (3) A的一个最短的子序列,它不是B的子串 (4) A的一个最短的子序列,它不是B的子序列 不存在输出-1,1<=len(A),len(B)<=2000. [算法]后缀自动机+序列自动机 [题解]后缀自动机用于识别 原文地址:https://www.cnblogs.com/onioncyc/p/8192581.html

BZOJ 2946 POI2000 公共串 后缀自动机(多串最长公共子串)

题意概述:给出N个字符串,每个串的长度<=2000(雾...可能是当年的年代太久远机子太差了),问这N个字符串的最长公共子串长度为多少.(N<=5) 抛开数据结构,先想想朴素做法. 设计一种稳定的暴力算法.可以想到这样一种做法:首先确定一个串,枚举每个位置,然后暴力计算其他每个串以这个位置开头的最长匹配,取最小值,就是在公共子串在我们确定下来的串的这个位置开头的时候所能得到的最长公共子串.不难发现把这个问题转化成后缀的形式也是一样的.同时发现可能在枚举多个位置的时候答案甚至最后构造出来的串都是

BZOJ 2946 Poi2000 公共串 后缀自动机

题目大意:求n个串的最长公共子串 太久没写SAM了真是-- 将第一个串建成后缀自动机,用其它的串进去匹配 每个节点记录每个串在上面匹配的最大长度 那么这个节点对答案的贡献就是所有最大长度的最小值 对所有贡献取最大就行了= = 这最大最小看着真是别扭 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 10100 using namesp

[BZOJ2946][Poi2000]公共串 后缀自动机

2946: [Poi2000]公共串 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1367  Solved: 612[Submit][Status][Discuss] Description 给出几个由小写字母构成的单词,求它们最长的公共子串的长度. 任务: l        读入单词 l        计算最长公共子串的长度 l        输出结果 Input 文件的第一行是整数 n,1<=n<=5,表示单词的数量.接下来n行每行一个单词

[BZOJ1396]识别子串 后缀自动机+线段树

1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 451  Solved: 290[Submit][Status][Discuss] Description Input 一行,一个由小写字母组成的字符串S,长度不超过10^5 Output L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长. Sample Input agoodcookcooksgoodfood Sample Output 1 2 3 3

BZOJ 1396&amp;&amp;2865 识别子串[后缀自动机 线段树]

Description 在这个问题中,给定一个字符串S,与一个整数K,定义S的子串T=S(i, j)是关于第K位的识别子串,满足以下两个条件: 1.i≤K≤j. 2.子串T只在S中出现过一次. 例如,S="banana",K=5,则关于第K位的识别子串有"nana","anan","anana","nan","banan"和"banana". 现在,给定S,求对于S的

后缀自动机初探

之前看过几次后缀自动机,然后因为人太蠢都没看懂. 最近重新填坑TAT... BZOJ4032: [HEOI2015]最短不公共子串 建出后缀自动机和序列自动机,然后我们知道自动机上每一条路径都相当于一个子串(子序列),这样只要从根节点开始bfs一遍,找到A有而B没有的,那就是字典序最小的辣. #include<cstring> #include<iostream> #include<algorithm> #include<cstdio> #include&l