最短不公共子串(后缀自动机+序列自动机+bfs)

最短不公共子串

题意

问最短的满足:是A的子串(子序列),且不是B的子串(子序列,子序列(子串))。(共四个问题)

思路

  1. 子串问题,考虑后缀自动机;子序列问题,考虑序列自动机;这不就成板子题了?
  2. 然后本题要求属于前者,而不属于后者的子结构,可以考虑暴力的在两种DAG上同时跑;若前者可以跑,后者却不能跑,说明此子结构仅属于前者,好像问题就解决了?
  3. 但仔细一想,长度为 2000 2000 2000的串子序列似乎太多了,好像不能跑完?这里我们考虑 b f s bfs bfs,记录哪些状态(二维)已经遍历;对于已经遍历过的状态,虽然此前在遍历时当前状态所代表的子结构可能不一样(比如序列自动机通过不同的路径到达某个节点),但此后节点的可到达性却只与当前节点是否可到达有关,而与怎样到达的无关,故后续节点是否可到达在之前的 b f s bfs bfs中已经处理好了;因此可以像普通的 b f s bfs bfs一样,遍历过的状态可接忽略掉!(使用 n × n n×n n×n v i s vis vis数组记录)
  4. 复杂度:后缀自动机为 O ( n ) O(n*∑) O(n);序列自动机为 O ( n ) O(n*∑) O(n);每个后缀自动机节点数为 2 n 2*n 2n,每个序列自动机节点数为 n n n b f s bfs bfs遍历时每个点遍历边数为 ,因此 b f s bfs bfs复杂度为 O ( n ) O(n*∑) O(n);
  5. 总复杂度为 O ( n ) O(n*∑) O(n),不过常数挺大。( 为字符集大小,等于 26 26 26

压行思路:由于后缀自动机与序列自动机都可以看做DAG,而在 b f s bfs bfs的时候也只需要用到边,因此在忽略这两种自动机的其他结构后,就变得一样啦!四个 b f s bfs bfs完全可以写在一起。

代码

#include "bits/stdc++.h"
using namespace std;

const int maxn = 4e3+10;

struct P{ int a, b, c; };
int ch[2][2][maxn][26], fa[2][maxn], len[2][maxn];
bool vis[maxn][maxn];
int tot[2]={1,1}, last[2]={1,1};
char s[maxn];

void add(int c, int f) {
    int p=last[f], np=last[f]=++tot[f];
    len[f][np]=len[f][p]+1;
    for(; p&&!ch[f][1][p][c]; p=fa[f][p]) ch[f][1][p][c]=np;
    if(!p) fa[f][np]=1;
    else {
        int q=ch[f][1][p][c];
        if(len[f][q]==len[f][p]+1) fa[f][np]=q;
        else {
            int nq=++tot[f]; len[f][nq]=len[f][p]+1;
            fa[f][nq]=fa[f][q]; fa[f][q]=fa[f][np]=nq;
            memcpy(ch[f][1][nq],ch[f][1][q],104);
            for(; p&&ch[f][1][p][c]==q; p=fa[f][p]) ch[f][1][p][c]=nq;
        }
    }
}

void pre(int f) {
    for(int i=strlen(s+1); i; --i) {
        memcpy(ch[f][0][i-1],ch[f][0][i],104);
        ch[f][0][i-1][s[i]-'a']=i;
    }
}

void bfs(int f1, int f2) {
    memset(vis,0,sizeof(vis));
    queue<P> q;
    q.push((P){f1,f2,0}); vis[f1][f2]=1;
    while(!q.empty()) {
        P now=q.front(); q.pop();
        for(int i=0; i<26; ++i) if(ch[0][f1][now.a][i]) {
            if(ch[1][f2][now.b][i]) {
                int a=ch[0][f1][now.a][i], b=ch[1][f2][now.b][i];
                if(!vis[a][b]) vis[a][b]=1, q.push((P){a,b,now.c+1});
            }
            else return (void)printf("%d\n", now.c+1);
        }
    }
    printf("-1\n");
}

int main() {
    scanf("%s", s+1);
    pre(0); for(int i=1; s[i]; ++i) add(s[i]-'a',0);
    scanf("%s", s+1);
    pre(1); for(int i=1; s[i]; ++i) add(s[i]-'a',1);
    bfs(1,1); bfs(1,0); bfs(0,1); bfs(0,0);
}
全部评论

相关推荐

04-12 21:52
南开大学 Java
鼠鼠有点摆,去年边学着没敢投简历,没实习。从1月到现在总共面了五次,四次字节的日常(HR打电话约面试才敢去的),然后一次腾讯的暑期,都是一面挂,其他则是没给面。暑期的岗,4.2才开始海投,前面想着等字节第四次一面后再投,结果挂,而且感觉投晚了。字节投了11个,9个简历挂,剩下2个没动静。阿里全都简历挂,剩下的在&quot;投递简历&quot;。腾讯给了一次面。然后其他大中厂、手机厂什么的都是做完测评or笔试就没下文,打开几个看也是终止流程,感觉剩下的也应该是简历挂了。感觉是简历的原因?项目部分,几次面试,感觉面试官主要就拷问过秒杀这一个点。自己说的时候会尝试把sse那条说成亮点,但除了腾讯面试官问过一下这整个点在业务方面对用户有什么用之类的问题外,其他最多只是问一下sse八股...感觉也许不是很让面试官感兴趣。这个短链接也是无人问津,就被问过一回雪花算法的设计。也许我该拿点评改改,然后再在网上找一个什么项目,凑两个,而不是用自己现在这两个项目?或者是点评改改放前面,然后原本第一个项目,把秒杀抽掉,剩下的想办法从网上火的RAG项目里移植点亮点,或者直接就用网上的RAG项目?感觉我主要还是偏向后端开发,但是感觉如果除开点评,再拿一个项目,想不到有什么自己能掌控且跟点评不重的。然后鼠鼠之前主要的问题是担心面试让打开项目演示,然后就一直花时间在用AI整第一个项目,第二个项目都没时间整,第四次面试之前还因为太害怕被认为不熟悉项目,跟AI一起把简历的说辞做了大幅度弱化,然后暑期都是拿弱化后的简历投的,感觉是不是看上去太没有吸引力就直接给简历挂了。(图1是弱化后的,图2是弱化前的,但之前3月初投了几家好像也是简历挂。)而且因为3月花了很多时间整在跟AI整代码,导致八股和算法都没怎么看,算法之前有跟灵神题单刷一些,还算入门,但是八股只看了一些基本的,可能面试的时候只答得上来60-70%,而且表述有些混乱,都是想到哪说到哪;前面几回面试基本上都有大板块的基础八股没答出来,比如RedisZ&nbsp;Set数据结构,MQ延时消息、可靠性保证,JVM内存分配的过程、GC&nbsp;roots,JUC锁,设计模式。现在有点不知道该怎么办。求大佬们给点简历修改建议或者面试准备建议,不胜感激!
何时能不做牛马:简历每个点之间的间距可以缩一下。几乎没遇到过要演示项目的情况,即使万一遇上了你也可以说部署在其他电脑上本地没代码。nku不应该简历挂吧?抓紧背背八股练练表达,不要放弃,五六月份找到也不晚(不然还得提前入职
应届生简历当中,HR最关...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务