JSOI2007 字符加密

把字符串倍长,求后缀数组后把前一半后缀对应的位置按序输出即可。

即使字符串有循环节也是不要紧的。

代码

Luogu2870 Best Cow Line

如果首尾字符不同就可以直接选择;如果相同,我们就比较这个前缀和后缀的字典序大小。可以证明这样是最优的。

那么把这个串反过来在后面接上,然后中间插入一个分隔符,建立后缀数组即可。

代码

CF30 E Tricky and Clever Password

我们考虑枚举中间点,manacher 计算最长回文串,那么整个串就划分为了,其中是回文串。那么问题就转化为了的前缀与的 LCP。表示 S 的反串。那么我们就把原串倒过来,求出每个前缀与反串前缀的匹配长度(KMP),然后做前缀 Max 即可。

时间复杂度

代码

CF873 F Forbidden Indices

建立 SAM 后,把没有被禁止的点加 1 的贡献,然后 fail 树上做前缀和就可以统计答案了。

代码

CF547 E Mike and Friends

题意:有个串。定义表示里出现的次数。有次询问,每次询问,问

考虑建立广义 sam,在插入一个串的时候在经过的所有结点上打上这个串的标记,表示这些状态以及它们在 fail 树上的祖先状态是这个串的子串。

那么在 fail 树上向上合并后,我们就得到了每个状态是哪些串的子串。那么询问就可以处理了。

使用线段树维护标记,线段树合并。复杂度

代码

NOI2018 你的名字

给出一个字符串,每次询问,问所有的本质不同的子串中,不在中出现的子串有多少个。

第一部分

首先考虑怎么做。

我们建立的 SAM 来将相同的子串归到一个状态里。

表示的后缀与的子串匹配的最长长度。对于的 SAM 中的状态,记录表示这个状态在中结尾的位置(可能有多个位置,选择任意即可)。记表示这个状态的长度(即)。

那么得到就是这个状态与匹配的最长长度。那么这个状态中不与匹配的部分就是。于是答案就是

第二部分

接下来考虑任意的情况。那么表示的就是的后缀与的子串匹配的最长长度。如果求出了那么我们就可以根据上面的式子求答案了。

我们考虑在的 SAM 接受的时候计算。具体地,记录一个表示当前与匹配的长度。每次我们尝试接受

  • 如果当前状态的后继状态中存在与匹配且包含于的状态,那么就接受并转移到后继状态,让自増,且
  • 否则我们让自减并再次检查是否存在匹配的状态。
  • 如果那么就跳转到状态。

如何判断当前状态的后继状态中是否存在与匹配且包含于的状态?对于每一个 SAM 上的状态记录一个集合表示所代表的子串在中出现的位置右端点的集合(即 Right 集合)。那么我们查询中是否存在区间里的元素,就可以判断是否存在与匹配且包含于的状态。

使用线段树维护即可,合并的时候使用线段树合并即可。

时间复杂度

代码