想学后缀自动机的 弱鸡 表示真自闭啊

开局一道题,内容全靠水 :Codeforces 427D

有题目才能更好地学 算法

题意很简单 :给两个字符串,求最短公共子串 的长度

后缀自动机 模板很多 ,给了各种写法 , 匡斌的版本 , 一个俄罗斯人的版本 , clj的版本 ,还有一种全数组(不用结构体)的版本

可真是太开心了(???我一个小白,到底哪个好理解??)

 ● 如果你是一个论文爱好者可以看看这个:https://blog.csdn.net/qq_35649707/article/details/66473069

全是俄文翻译,全是概念

 ● 而民间大神版本,看了这么多,对原理   和    应用    所做的中文解释  能看懂  且没歧义的版本:

https://blog.csdn.net/doyouseeman/article/details/52245413

各路大神是很厉害 , 可是冗长的一句话,断句阅读  给个空格  可好,逗号根本不顶事啊(很难受)

经过切身实践,我表示  数组 版本的最好理解,问我为什么?

(问我数组版本在哪学到的?下文讲解里有个链接,在那看到的)

(注:节点只表示状态,而边表示字母)

你看这通俗的数组名称

  1. son 当前状态节点 通过字母x  推往下一个状态节点 ( 每一个状态节点都有一个角标last,这个角标一直在增加 )
  2. pre 当前状态节点   前推上一个  可更新状态的  角标
  3. step[i]  从空串0  到  状态i 的最长子串 长度
  4. total 总共有多少个状态节点 (数量)

对应结构体版本

  • len - step
  • link - pre
  • son - next 用map写或者next[26]都行
  • last - last 还是当前最后一个状态节点
  • total - sz 状态节点总数

所以构造方式。。。我们还是用结构体(真香)

struct state {int len,link;map<char,int> next;
};
state st[Maxn*2];
int sz, last;

要注意的一点是 , 如果只是一个单独的字符串建立后缀自动机 , 大佬们说只要用Maxn * 2 个节点就够了

那么首先我们要进行初始化操作

void sa_init() {sz = last = 1;
}

状态节点 以后简称 节点

一开始什么都没有,我们把 1节点 定义为 一个空串  的状态节点

然后就是在线O(n)建树的过程

因为我们要一个一个字母的往树里加,所以下面的sa_extend 函数 就是一个字母  往里加 过程

里面有一些注释:帮助理解,后面的AC代码 ,除新加的注释 , 当前帮助学习的注释都会去掉

void sa_extend (char c) {int p = last;//一个用来  推  状态的节点下标int np = last = ++sz;//更新last ,同事新建一个 经过  边c  的  np新状态st[np].len = st[p].len + 1;for (; p && !st[p].next[c]; p=st[p].link)st[p].next[c] = np;if (p == 0){st[np].link = 1;} else {int q = st[p].next[c];if (st[p].len + 1 == st[q].len){st[np].link = q;} else {int nq = ++sz;st[nq].next = st[q].next;st[nq].len = st[p].len + 1;st[nq].link = st[q].link;st[q].link = st[np].link = nq;for (; p && st[p].next[c]==q; p=st[p].link)st[p].next[c] = nq;}}
}

np 是当前 即将 加到  树里的 新节点

所以接下来到此结束

直接讲一下为什么这么建立后缀树

首先我们要了解

  • 说是后缀树  ,但是它从根节点  遍历边  按一条线路  顺序输出的   还是  某个子串的  正顺序,并不是逆顺序

惊不惊喜,但是它实际是某一个 节点   掌管    其所有之前  能控制的  节点

什么叫能控制? 从源点走到之前的所有状态  都没出现过   c   字符的时候 , c就能控制之前所有的状态

那么如果出现了,某一个状态节点 ,它的下一条边已经出现了 c ,这就是到了 当前的新节点 所不能掌控的 节点了

那么分两种情况

看了这么多后缀自动机博客 , 这一部分讲的最好的 ,最容易懂得 就是

但是在这里面 , np 实际是用cur来表示的 , 而且新建的 节点是 clone 代替上面代码里的 q

http://blog.sina.com.cn/s/blog_70811e1a01014dkz.html#cmt_533F66DF-7F000001-75D82355-8B8-8A0

这位大佬的讲解。(后文还有新的东西)

欢迎回来。。

接下来我们回到题目上来 , 应用后缀自动机

还记得吧

这里面我们应对两个字符串建立的 一个后缀自动机

但是建立自动机的时候有一个问题,第二个字符串 再次从 1节点   开始创建

那么一些重复的 会再次被创建怎么办

//因为要建两次树,重复的就要跳过
if(st[last].next[c] && st[last].len + 1 == st[st[last].next[c]].len){last = st[last].next[c];return;
}

这一段就是检验  相邻的两个连续节点  是不是可以取代 新创建的过程(其实看代码更容易懂)

思路中用到了  拓补  我一个小白怎么知道拓补? 我看了一堆(真的是一堆)博客才发现的,用这个的人没说明这一步是干什么的

他从拓补序从后往前推同时更新了其中的长度 , endpos是拓补数组标记

什么时候更新答案?建立自动机的时候会标记这个  状态节点是  s1串s2串 或者 两个经过的 节点,如果这个节点两个串都更新到了,那么就可以和ans 去个min ,更新到ans。

大佬们还是大佬,写的代码都不解释,直接上代码

反正我的第一道后缀自动机就这么自闭的结束了 , 还要做这类型的题才能真的融会贯通吧

AC代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
#include <cmath>
#include <map>
#include <queue>
#include <algorithm>
#include <set>
#include <vector>
#include <stack>
#define Clear( x , y ) memset( x , y , sizeof(x) );
#define Qcin() std::ios::sync_with_stdio(false);
using namespace std;
typedef long long LL;
const int Inf = 1e9 + 7;
const int Maxn = 5007;
int N , M;
char s[Maxn];
struct SAM{int endpos[2][Maxn*4];int a[Maxn*4] , b[Maxn*4];struct state {int len,link;map<char,int> next;void init(){len = 0 , link = 0;}};state st[Maxn*4];int sz, last;void sa_init() {sz = last = 1;}void sa_extend (char c) {//因为要建两次树,重复的就要跳过if(st[last].next[c] && st[last].len + 1 == st[st[last].next[c]].len){last = st[last].next[c];return;}int p = last;int np = last = ++sz;st[np].len = st[p].len + 1;for (; p && !st[p].next[c]; p=st[p].link)st[p].next[c] = np;if (p == 0){st[np].link = 1;} else {int q = st[p].next[c];if (st[p].len + 1 == st[q].len){st[np].link = q;} else {int nq = ++sz;st[nq].next = st[q].next;st[nq].len = st[p].len + 1;st[nq].link = st[q].link;st[q].link = st[np].link = nq;for (; p && st[p].next[c]==q; p=st[p].link)st[p].next[c] = nq;}}}void Topo(){int os = 1;for(int i = os ; i <= sz ; i++){++a[st[i].len];}for(int i = os ; i <= sz ; i++){a[i] += a[i-1];}for(int i = os ; i <= sz ; i++){b[a[st[i].len]--] = i;}}void solve(){int ans = 9999;//cout << "# sz = " << sz << endl;for(int i = sz ; i > 1 ; i--){int e = b[i];//if(st[e].link == -1)   continue;if(endpos[0][e] == 1 && endpos[1][e] == 1){ans = min(ans , st[st[e].link].len + 1);}//cout << "ans = " << ans << endl;endpos[0][st[e].link] += endpos[0][e];endpos[1][st[e].link] += endpos[1][e];}if(ans == 9999)    printf("-1\n");else   printf("%d\n",ans);}
}sam;int main()
{scanf(" %s",s);int len = strlen(s);sam.sa_init();for(int i = 0 ; i < len ; i++){sam.sa_extend(s[i]);sam.endpos[0][sam.last] = 1;}//cout << sam.sz << " " << sam.last << endl;sam.last = 1;scanf(" %s",s);len = strlen(s);for(int i = 0 ; i < len ; i++){sam.sa_extend(s[i]);sam.endpos[1][sam.last] = 1;}sam.Topo();sam.solve();return 0;
}

后缀自动机(探索)Codeforces 427D相关推荐

  1. CodeForces - 427D Match Catch(后缀数组/广义后缀自动机)

    题目链接:点击查看 题目大意:给出两个字符串,求出两个字符串中的最短公共子串,且在每个字符串中只出现过一次 题目分析:因为这个公共子串只能在字符串中出现一次,考虑到用后缀数组,我们先将两个字符串通过特 ...

  2. Codeforces 235C Cyclical Quest (后缀自动机)

    题目链接: https://codeforces.com/contest/235/problem/C 题解: 对大串建后缀自动机 对询问串复制拆环.这里一定要注意是复制一个循环节不是复制整个串!循环节 ...

  3. Codeforces Round #364 (Div. 1) (差一个后缀自动机)

    B. Connecting Universities 大意: 给定树, 给定2*k个点, 求将2*k个点两两匹配, 每个匹配的贡献为两点的距离, 求贡献最大值 单独考虑每条边$(u,v)$的贡献即可, ...

  4. CodeForces 235C Cyclical Quest (后缀自动机)

    题意:给一个主串,再给出多个模式串,分别求主串中有多少个连续子串,与模式串循环同构. 题解:后缀自动机 因为要求循环同构,所以将模式串复制放到后面.(要么加终止符,要么传入长度) 先对主串建sam,然 ...

  5. Codeforces #235 C.Cyclical Quest(后缀自动机)

    传送门 题意:给定一个模式串和nnn个匹配串,询问原串有多少个子串和匹配串循环同构 考虑要求循环同构,于是先对SSS建出后缀自动机 把每次询问的XXX倍长在自动机上跑 如果当前匹配的长度已经超过原串长 ...

  6. CF 316G3 Good Substrings——广义后缀自动机

    题目:http://codeforces.com/contest/316/problem/G3 对询问串和模式串一起建一个后缀自动机,做出在每个串上的 right 集合大小之后枚举自动机上的每个点看看 ...

  7. 【POJ1509】Glass Beads 【后缀自动机】

    题意 给出一个字符串,求它的最小表示法. 分析 这个题当然可以用最小表示法做啦!但是我是为了学后缀自动机鸭! 我们把这个字符串长度乘二,然后建SAM,然后在SAM上每次跑最小的那个字母,找出长度为n的 ...

  8. bzoj 2946 [Poi2000]公共串——后缀自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2946 对每个串都建一个后缀自动机,然后 dfs 其中一个自动机,记录同步的话在别的自动机上走 ...

  9. 后缀自动机 ---- P3804 【模板】后缀自动机(求每个等价类最长串的出现次数)

    后缀自动机一些关键点 首先后缀自动机上面每个节点都是一个等价类并且是最长的字符串的结尾 后缀自动机上的fail链反建就是parent tree,下面是SAM和Parent tree的构造 对于这道模板 ...

最新文章

  1. Matlab | Matlab从入门到放弃(6)——数组
  2. 7-1 最大子列和问题 (20 分)
  3. 幻读(phantom read)
  4. comsol线圈不能加电流激励_comsol里面如何绘制三维的激励线圈?
  5. [转]spring入门(六)【springMVC中各数据源配置】
  6. 线程与进程最通俗易懂的解释(附面试题与答案)
  7. CMOS摄像头驱动分析笔记1
  8. ROVIO安装运行及保存轨迹用evo评估
  9. 虚拟机配置NAT网络后ping不通
  10. YOLOv8 从环境搭建到推理训练
  11. C++检测键盘某键是否按下
  12. UED团队规范设计参考及建议
  13. Multisim基础 示波器 波形沿Y轴正方向上移一格
  14. 《网站分析实战--如何以数据驱动决策,提升网站价值》学习笔记
  15. pip安装Cartopy小白版
  16. 【数据架构】SOGAF 通用实体框架 (CoE)
  17. 关于b站M_Studio的学习笔记
  18. 选择私有云解决方案,需要考虑哪些要素
  19. FreeBSD13.0 安装配置中存在的一些问题
  20. JS 数组转字符串,字符串转数组方法汇总

热门文章

  1. 电子拍卖系统开发第一天
  2. Windows局域网或PC网线直连中不能被ping通的问题
  3. iphone 添加qq邮箱.
  4. svn版本管理软件——svn日常管理代码
  5. Excel 2010 VBA 入门 072 工作表事件之activate
  6. 干货分享:高效办公工具【视频转文字、视频播放器、B站视频下载软件、贴图、截图提取文字并翻译、笔记记录软件、任务管理网站】
  7. 解析——详细解读微信内置浏览器不支持下载APK(APP)软件的解决方法
  8. 二叉树的前序遍历(递归)
  9. [题解]CSP-分蛋糕(线性表)
  10. 【学术篇】luogu1351[NOIP2014 提高组]联合权值