后缀自动机

后缀自动机的疑难点

代码

void sam_extend(char c) {
  int cur = sz++;
  st[cur].len = st[last].len + 1;
  int p = last;
  while (p != -1 && !st[p].next.count(c)) {
    st[p].next[c] = cur;
    p = st[p].link;
  }
  if (p == -1) {
    st[cur].link = 0;
  } else {
    int q = st[p].next[c];
    if (st[p].len + 1 == st[q].len) {
      st[cur].link = q;
    } else {
      int clone = sz++;
      st[clone].len = st[p].len + 1;
      st[clone].next = st[q].next;
      st[clone].link = st[q].link;
      while (p != -1 && st[p].next[c] == q) {
        st[p].next[c] = clone;
        p = st[p].link;
      }
      st[q].link = st[cur].link = clone;
    }
  }
  last = cur;
}

构造后缀自动机

case1,p沿着后缀链接树向上跳,涉及到的每个p没有st[p].nxt[c]的转移

  • 从后缀链接树的last状态向上跳,会遍历到长度为[0,len(last)]的所有区间,即这个字符串的所有后缀,若跳到了根节点也没有c的转移,那么就是整个SAM中不存在字符c,那么为p的每一个c转移都指向新加入的那个点,后缀链接树上,将longest(last)+c的后缀链接指向根节点,这是独一无二的endpos

case2,p沿着后缀链接树向上跳,找到一个pq的转移c,并且满足len(p)+1=len(q)

  • 后缀链接树向上找,找到的每个状态都是last的后缀,如果这个状态p通过转移c正好能够走到q,并且q是它所属的endpos等价类中最长的,那么显然q不会在前方相比p突出一块,会与p对齐,而plast的后缀,p+cq必然是last+c的后缀,所以last+cendpos必然是q的一个真子集,而由于我们找的是顺着后缀链接树的第一个满足条件的p,它必然是最长的,又由于q也是它对应的endpos等价类中最长的,所以last+c不断地删头endpos首次发生变化必然是删到len(q)时,所以直接相连是没有问题的

  • 为什么找到一个p就停下来?因为如果我们继续向上跳,跳到的必然是p的后缀,记为p,所以它通过转移c对应的状态q也一定是q的后缀,那在后缀链接树上,q一定是q的父亲,由于后缀链接树上,儿子的endpos集合是父亲的真子集,所以在q的儿子接上位置n之后,由定义,它的所有的父节点的endpos集合都顺水推舟、水到渠成的拥有了位置n,所以只连一次就符合了SAM的定义

case3len(p)+1<len(q) 怎么搞呢

  • 我们发现,不同于上一个情况,我们的q状态的前端和p状态相比突出了一块,而这时候我们找到的last的最大的endpos集合不同的后缀是p而不是q,因此增加c之后,仅仅有p+c的状态的endpos集合增加了n,我们记这个状态为nq,可以发现它实际上是q状态中最短的子串,所以合着q状态中只有最小的nqendpos增加了。怎么办呢?我们考虑将q状态拆开,将到q的转移c全部指向nq,与此同时,q的所有转移,nq也一定继承。这很好理解,因为nq实际就是q的后缀,在它后面加什么defg ,所指向状态的endpos都不会受影响,nqq还是一家人(一家人再怎么打骂起分歧也不会分开),所以这么连肯定也是对的

  • 下面考虑后缀链接树怎么搞,首先一个显而易见的结论,nqendpos集合仅仅只比qendpos集合多了一个元素n,这里好办,让nq代替q的位置,然后把qlast+c的后缀链接指向nq(为什么能取代,儿子取代父亲的位置不是恒河里吗doge),然后我们通过后缀链接树向上跳,如果发现st[p].nxt[c]==q,那么就在SAM上把这个p的转移c直接连到q上("将到q的转移c全部指向nq",对应的就是上文的这句话),因为这时跳到的p+cendpos集合包含n,它显然不应该指向q。如果发现st[p].nxt[c]q,这句话是什么意思呢?就是我们的q已经足够小了,此时跳到的p是起初p的后缀,再增加一个字符c会形成一个更大的endpos集合,增加字符后形成的q显然也是nq的后缀,接着向上跳形成的q也同样是nq的后缀。所以我们发现,再往上走都是q的祖先,而由于nq的存在,上面所有的状态的endpos显然都包含有n,这时p再向q,q,连边就没有一个状态两种endpos集合的情况了,所以它是对的

复杂度证明

  • 空间复杂度显然正确,因为每一次插入字符时,Sam的状态数只会增加两个,因此为O(n),上界为2n-1,在形如字符串abbbb时取到,这就是为什么SAM的空间要开两倍

  • 时间复杂度,分两种情况讨论

(1). 考虑连续的转移,我们假设能连续就连续,总共就不超过2n1个状态,那么将这些点连续地连起来最多也就2n2条边,实际上,极限情况abbbb也达不到2n2

(2). 考虑不连续的转移,令当前不连续的转移为(p,q),字符为c,我们设从起始点到p的最长路径为u,q到任意终止点的最长路径为w,那么u+c+w这个东西显然是原串的一个后缀,而一个长为n的串最多就有n个后缀,SAM中又不会出现重复的子串,因此不连续转移的个数最多就是n1

posted on   star_road_xyz  阅读(92)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示