Processing math: 18%

Pluto_Rabbit's Blog

蒟蒻妹纸一枚,轻拍

【学习】字符串的各种-SAM

Jacinth posted @ 2016年9月11日 16:45 in Lerning with tags 字符串 学习 后缀自动机 Learning , 900 阅读

想来SAM也真是太神辣

感觉跳坑里就死在里面出不来了

所以把它拎出来再学(bian)习(shi)一下

后缀自动机可以被理解为所有子串的简明信息。后缀自动机以压缩后的形式包含了一个长度为n的字符串的所有信息,仅需要O(n)的空间。并且,它能在O(n)时间内被构造。(人家都O(n)了没法无视啊!!!)

SAM 是处理字符串相关问题的有力工具, 可以解决包括但不限于以下问题:

  • 判断W是否为T的子串
  • 判断W是否为T的后缀
  • 计算T的不同子串个数
  • 计算T的不同子串的总长度
  • 计算W在T中的出现次数
  • 查找W在T中第一次出现位置
  • 查找W在T中所有的出现位置
  • 查找T1&T2的最长公共子串
  • 查找T1到Tk的最长公共子串
  • 查找T的最小循环节
  • 查找T的第k小子串
  • 查找最短的不是T的子串的字符串
  • ......

 

STATE

我们定义endpos(T,s)为T中s出现位置终点的集合。且当endpos(T,s1)==endpos(T,s2) 我们就称s1s2终点等价
我们可以知道,某个状态u,可以识别T的某个子串的集合, 设这个集合为substrings(u)substrings(u)中的两个子串都是终点等价的。
考虑对于T的任意两个子串s1,s2而言,endpos(T,s1),endpos(T,s2)之间的关系(假设length(s1)length(s2)):
endpos(s1)endpos(s2),即s1,s2同时以T的某个字符为结尾,那么s1必为s2的后缀(含s1==s2
同时,若s1s2的后缀,那么endpos(s1)endpos(s2)(子串长度越长队endpos的限制越多导致endpos的集合越小),即endpos(s1)endpos(s2)
所以 endpos(s1)endpos(s2)s1
因而我们只剩下了两种状况

\begin{cases} endpos(s_1) \supseteq endpos(s_2), & \text{if } s_1 \sqsupset s_2 \\ endpos(s_1) \cap endpos(s_2) = \emptyset, & \text{otherwise} \end{cases}

我们发现由于同一substrings(u)下最长子串和最短子串间的endpos相等,则最短子串为最长子串的后缀,对于所有substrings(u)下的s_i我们可以发现它都是substrings(u)的后缀,同样的也都是最长子串的后缀。
所以这就意味这每一个状态都对应一段连续的后缀,(很厉害的样子)

根据之前的内容,对于一个状态u我们可以给出:

  • longest(u) : substring(u)中最长的子串
  • shortest(u): substring(u)中最短的子串
  • maxlen(u): longest(u)的长度
  • minlen(u): shortest(u)的长度

而一个状态u可以由以上四个来表示。

 

SUFFIX LINK

我们给定两个状态u,v 那么 v = link(u)即为uv的suffix link 且 maxlen(v) + 1 = minlen(u). suffix link 可以把状态之间用suffix link 连起来

 

STATE'S TRANSITIONS

我们给定一个状态u和标记ctrans(u,c) = v表示状态u到状态v存在状态转移路径。

我们可以知道以v为终点的转移都有一个相同的标记。

U_v = \{ u_i \mid trans(u_i, c) = v \}

我们之前知道对于一个状态v,substring(v)中的子串为连续的,又由于状态v的转移都有相同的符号,那么\{ s_i \mid s_i \in substrings(u_i), u_i \in U_v \}也必为连续的。

 

SAM的构造步骤

我们对于SAM中的每个状态,维护以下内容

  • maxlen(u)
  • link(u)
  • trans(u,\Sigma)[以u为起点的状态转移]

构造过程:

  • 新建状态np,使maxlen(np) = maxlen(last) + 1
  • 从状态last开始沿着suffix-link path访问每个状态,直到p = null或存在trans(p,T_i),令trans(p,T_i) = np
  • 判断p是否为null,
    • p = null,则link(np) = st, la = np 返回第一步
    • 否则p为某个存在的状态且trans(p,T_i) = q
      • maxlen(p)+1 = maxlen(q)link(np) = q, la = np 返回第一步
      • maxlen(p)+1 < maxlen(q) 则从q状态拆出状态nq,令link(nq) = link(q) , link(q) = link(np) = nq, la = np 返回第一步

萌萌哒的extend代码QAQ:

 

其他信息的维护

  • minlen(u)
    • v = link(u) \implies maxlen(v) + 1 = minlen(u) 直接在更新link(u)时更新minlen(u)就可以了
  • first\_endpos(u) 表示 endpos(u)中最小的下标
    • 对于状态u, endpos(u)的变更伴随着指向状态u的suffix-link的建立。所以我们首先维护first\_endpos(u),在后续的过程中,我们只用沿着suffix-link反向DFS可得endpos(u)
    • 对于first\_endpos(u)的维护,我们只用
      • 在新建状态np时,设first\_endpos(np)=i
      • 在新建状态nq时,设first\_endpos(nq)=first\_endpos(q)
  • accept(u) 表示接收节点
    • 我们只用看u是否在从last的suffix path上即可。因此我们可以在创建SAM后扫一遍suffix path就可以得到所有的接收状态

似乎to be continued...

 

各种各样的参考资料:

先搬了几道题(无视奇奇怪怪的时间线吧qaq)

BZOJ-3998 

Descriprtion 对于一个给定长度为N的字符串,求它的第K小子串是什么。
Input

第一行是一个仅由小写英文字母构成的字符串S

第二行为两个整数T和K,T为0则表示不同位置的相同子串算作一个。T=1则表示不同位置的相同子串算作多个。K的意义如题所述。

Output 输出仅一行,为一个数字串,为第K小的子串。如果子串数目不足K个,则输出-1
Sample Input

aabc

0 3

Sample Output aab

BZOJ-4516: [Sdoi2016]生成魔咒

Descriprtion
求每次操作后不同子串个数

听说有SA的做法飞起来

但是由于在学习SAM就用SAM了呀(明明SAM简单辣

其实更新的时候加上len[i]-len[fa[i]]就可以了(板子里是mx[i]-mx[fa[i]]

BZOJ-4566: [Haoi2016]找相同字符

Descriprtion
给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两个子串中有一个位置不同。
Input
两行,两个字符串s_1,s_2,长度分别为n_1,n_21 \leq n_1, n_2 \leq 200000,字符串中只有小写字母
Output
输出一个整数表示答案
Sample Input
aabb
bbaa
Sample Output
10

每个节点对答案的贡献为 (mx[x]-mx[fa[x]]) \times sz[0][x] \times sz[1][x]

HDU5343 MZL'S Circle Zhou

Descriprtion
给定两个字符串A,B,分别截取其中的子串X和Y,连接组成X+Y。
求不同的X+Y的方案数。

分别对AB建立后缀自动机

A进行查找X,当X的末尾不能接上字符i,那么就到B中查找所有以i为开头的子串

在两个DAG上DP就可以了【撒花

(答案用unsigned long long存啊啊啊啊,傻逼的我一直死在这里不知道多少次了

BZOJ-4032:[HEOI2015]最短不公共子串

Descriprtion
给两个小写字母串A,B,请你计算:
(1) A的一个最短的子串,它不是B的子串
(2) A的一个最短的子串,它不是B的子序列
(3) A的一个最短的子序列,它不是B的子串
(4) A的一个最短的子序列,它不是B的子序列
Input

两行,小写字母表示的字符串A和B

Output 输出4行,每行一个整数,表示以上4个问题的答案的长度。如果没有符合要求的答案,输出-1.
Sample Input
aabbcc
abcabc

 

Sample Output
2
4
2
4

四个小问,答案都是最小公共子××+1

感觉非常厉害

对于子串,在建立的后缀自动机上跑匹配

对于子序列,采用DP的贪心来做,la[i][j]表示第i位后第一个j出现的位置

然后用BFS四(fu)合(zhi)一就可以了

BZOJ-3238: [Ahoi2013]差异

我们需要一个后缀树,然后两个后缀的lcp就是它们lca的len,我们在后缀树上进行DP就可以了。

后缀树呢我们可以通过反序后缀自动机得到。

BZOJ-3926

神奇的题目描述啊。。。感觉语文不好的后果来了

ZJOI的题目呢。。。马上省选了好虚。。强省蒟蒻滚粗无疑辣。。

BZOJ-4199

唔。。2015年的NOI题目呢。。。别忘了考虑负数。。

Avatar_small
PSEB 10th Class Ques 说:
2022年9月05日 12:50

Punjab School Education Board came into Existence Through a Legislative Enactment in November 1969 for the Development and Promotion of School Education in the state of Punjab. PSEB is Going to Conduct PSEB 10th Class Question Paper the Matric Examination month of March to April 2023 under Punjab State Government. PSEB is published in the Matric Exam Model Question Paper 2023 Blueprint at Official Website


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter