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

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.

对B建出后缀自动机和序列自动机

(1)(2)问直接枚举A子串的左端点,找在自动机上第一个不能匹配到的右端点

(3)(4)问用f[i][j]表示考虑了A串的前i个字符,在自动机上匹配到节点j的最短子序列长度,答案为f[len(A)][null]

时间复杂度O(len(A)*len(B)+len(B)*26)

#include<cstdio>
#include<cstring>
const int inf=0x3f3f3f3f;
char s1[2007],s2[2007];
int nx[4111][26],l[4111],fa[4111],pv=1,ptr=1,f[4007],g[4007];
int snx[2007][26],sp=1;
void mins(int&a,int b){if(a>b)a=b;}
int main(){
    scanf("%s%s",s1+1,s2+1);
    int l1=strlen(s1+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){
        int x=s2[i];
        for(int j=sp++;j&&!snx[j][x];--j)snx[j][x]=sp;
        int p=pv,np=++ptr;pv=np;
        l[np]=l[p]+1;
        while(p&&!nx[p][x])nx[p][x]=np,p=fa[p];
        if(!p)fa[np]=1;
        else{
            int q=nx[p][x];
            if(l[q]==l[p]+1)fa[np]=q;
            else{
                int nq=++ptr;
                memcpy(nx[nq],nx[q],26);
                l[nq]=l[p]+1;
                fa[nq]=fa[q];
                fa[q]=fa[np]=nq;
                while(p&&nx[p][x]==q)nx[p][x]=nq,p=fa[p];
            }
        }
    }
    int ans=inf;
    for(int i=1;i<=l1;++i){
        for(int j=i,w=1;j<=l1;++j){
            w=nx[w][s1[j]];
            if(!w){
                mins(ans,j-i+1);
                break;
            }
        }
    }
    printf("%d\n",ans==inf?-1:ans);
    ans=inf;
    for(int i=1;i<=l1;++i){
        for(int j=i,w=1;j<=l1;++j){
            w=snx[w][s1[j]];
            if(!w){
                mins(ans,j-i+1);
                break;
            }
        }
    }
    printf("%d\n",ans==inf?-1:ans);
    memset(f,0x3f,sizeof(int)*(ptr+1));
    memset(g,0x3f,sizeof(int)*(ptr+1));
    f[1]=g[1]=0;
    for(int i=1;i<=l1;++i){
        int x=s1[i];
        for(int j=1;j<=ptr;++j){
            mins(g[nx[j][x]],f[j]+1);
        }
        memcpy(f,g,sizeof(int)*(ptr+1));
    }
    printf("%d\n",f[0]==inf?-1:f[0]);
    memset(f,0x3f,sizeof(int)*(sp+1));
    memset(g,0x3f,sizeof(int)*(sp+1));
    f[1]=g[1]=0;
    for(int i=1;i<=l1;++i){
        int x=s1[i];
        for(int j=1;j<=sp;++j){
            mins(g[snx[j][x]],f[j]+1);
        }
        memcpy(f,g,sizeof(int)*(sp+1));
    }
    printf("%d\n",f[0]==inf?-1:f[0]);
    return 0;
}
时间: 2024-10-24 21:23:51

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

【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

【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

poj3080(Blue Jeans)kmp求多个串公共子串

题意:给出1-10个长度为60的字符串,求出最长的公共子串(长度不能小于3),如果有多个一样长的,输出字典序最短的. 解法:想到kmp时,自己第一反应枚举第一个串的所有子串,在其他所有串中走一遍kmp,复杂度为10*60*60*60,但是发现只需枚举第一个串后缀就可以,每次枚举记录在所有串能走最远中走的最短的那个长度.这样复杂度就成了10*60*60,0ms AC. 代码: /**************************************************** * autho

后缀数组(多个字符串的最长公共子串)—— POJ 3294

对应POJ 题目:点击打开链接 Life Forms Time Limit:6666MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description Problem C: Life Forms You may have wondered why most extraterrestrial life forms resemble humans, differing by superficial tra

编辑距离和最长公共子串

编辑距离和最长公共子串问题都是经典的DP问题,首先来看看编辑距离问题: 问题描述 Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.) You have the following 3 operations permitted on a word: a) Insert a

最长公共子串、最长公共子序列的Java实现与NLP应用

前言以前HanLP使用"最短编辑距离"来做推荐器,效果有待提高,主要缺点是根据拼音序列的编辑距离推荐的时候,同音字交错很常见,而编辑距离却不那么大.这时我就在寻求一种补充的评分算法,去评判两个句子在拼音这一维度上的相似程度.区别最长公共子串(Longest Common Substring)指的是两个字符串中的最长公共子串,要求子串一定连续.最长公共子序列(Longest Common Substring)指的是两个字符串中的最长公共子串,不要求子串连续.求解两者的求解与编辑距离一样,

最长公共子串

(连续) - 阿里笔试[分析+编码] 题目描述:给定一个query和一个text,均由小写字母组成.要求在text中找出以同样的顺序连续出现在query中的最长连续字母序列的长度.例如,query为“acbac”,text为“acaccbabb”,那么text中的“cba”为最长的连续出现在query中的字母序列,因此,返回结果应该为其长度3.请注意程序效率. [思路]用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0.然后求出对角线最长的1序列,其对应的位置

一天一道算法题(5)---最长公共子串

题目 给定两个字符串str1和str2,返回两个字符串的最长公共子串.例如:str1="1AB2345CD",str2="12345EF",公共子串是"2345" 解析 最长公共子串和最长公共子序列的区别是,子串是连续的,子序列是不连续的. 首先还是要生成动态规划表.生成大小为M*N的矩阵dp.dp[i][j]的含义是,在必须把str1[i]和str2[j]当作公共子串最后一个字符的情况下,公共子串最长能有多长.比如,str1="A12