大意:
给定 n 个数,求这 n 个数两两异或的值中的前 k 小
解:
将加法换成异或就变成了一个用堆合并多个有序表的经典问题,对于加法我们按大小排序就能得到有序表,而由于这里是异或,我们需要高效地维护有序表,即对于一个数 ai 快速求出与它异或第 k 小的数。
我们将所有数按二进制建成 Trie ,然后在 Trie 的结点上记录下子树中的结束结点个数,再在 Trie 树上走一遍就得到了答案
大意:
给定 n 个字符串,对于每个字符串求以这个字符串为后缀的字符串中第 k 小的编号
解:
反转建出字典树,相当于询问这个点的子树里结束结点编号的第 k 大,子树查询利用 dfs 转区间第 k 大,主席树即可
大意:
维护一个序列,支持以下操作:
1. 在某个位置插入一个数
2. 删除某个位置上的数
3. 修改某个位置上的数
4. 求某段区间中的次大值与区间中另一个数的异或值的最大值
解:
假如是询问整个序列,我们可以用字典树来做,由于每次询问区间不一样,我们用平衡树套字典树维护区间信息,这里用替罪羊来套字典树
大意:
给一个字符串 S(|S|<=106) ,求所有前缀 Si 的 numi 之积, numi 为 Si的前缀S′的数量,使得该前缀S′与Si的后缀相同且不重叠
解:
类比 KMP求fail数组的 算法,我们可以在线性的时间内求出每个位置的最长不重叠的与后缀相同的前缀的结束位置,做法一样。然后我们记录一下每个位置有多少个前缀与后缀相同即可。
大意:
给一个循环产生的字符串,求最小循环节
解:
脑补一下可以发现答案就是 n−fail[n]
首先这一定是一个合法的解
其次一定不存在更小的循环节,否则 fail[n] 会更小
因此这就是最小循环节的长度
大意:
给定长度为 m 的数字串 S ,求不包含子串 S 的长度为 n 的数字串 T 的数量
解:
这种计数问题我们考虑 dp 来解决,设计状态时我们发现除了需要知道当前是 T 的第几位,还需要知道当前的 T 与 S 的匹配情况,因为此我们记状态 f[i][j]为T的第i位,与S长度为j的前缀匹配 ,那么转移应该是枚举下一位填充的字符 ch 转移到 f[i+1][j′] ,我们需要快速的知道对于某个 Sj 在之后多了一个字符 ch 将与哪个前缀匹配,这其实就是字符串的匹配问题,由于只有一个模板串,我们用 kmp 解决。最后我们发现对于任意的 i,i−>i+1 的转移对于第二维是不变的,因此我们矩阵乘法加速这一过程
大意:
给定一个多边形,求对称轴数量
解:
这里要用到一个神奇的想法。
对于一个多边形,我们先找到它的中心,然后我们就可以将这个多边形序列化,具体做法是这样:任选一个顶点 i ,与中心 O 的连线 Oi 的长度作为序列的第一个元素,顺时针取下一个顶点 i+1 ,取 Oi与Oi+1 的夹角为第二个元素, Oi+1 的长度为第三个元素,然后这样边角边角的不断循环取遍所有定点就得到了一个序列
我们先顺时针得到一个序列并将将序列倍增。再逆时针扫得到另一个序列,逆时针序列在顺时针序列中的出现次数就是对称轴数量,这个问题我们可以用 kmp 解决
大意:
给定一个字符串,求这个字符串有多少个子串满足这个子串可以拆分成 ABA 的形式,其中 |A|>=k,|B|>=1
解:
这种前缀与后缀相同很容易使我们联想到 kmp
枚举一个子串的左端点,对于一个右端点,我们只需要判断它是否存在长度在 [k,2×(|S|−1)] 之间的前缀与后缀匹配,那我们求出长度不超过上界的最长前后缀,判断是否满足下界即可
大意:
给定一个单词表和 m 个字符串,问每个字符串的最长的前缀,满足这个前缀可以拆分成一些字符串 使这些字符串都在单词表中出现过
解:
对每个字符串我们考虑 dp ,记 f[i] 表示长度 为i 的前缀能否成功拆分,那么关键问题在于找出所有以 i 为结尾所出现的单词。
我们将所有单词建成 AC 自动机,在表示单词结尾的结点上记录单词的长度,将一个串放在自动机上运行,一旦我们转移到一个表示单词结尾的结点,我们就找到了当前位能匹配的最长单词,沿 fail 一路往上就能找到当前位结束的所有单词,然后处理 dp 即可。
大意:
初始字串为空,首先给定一系列操作序列,有三种操作:
1. 在结尾加一个字符
2. 在结尾删除一个字符
3. 打印当前字串
然后多次询问第 x 个打印的字串在第 y 个打印的字串中出现了几次
解:
首先我们将所有打印的字符串建 AC 自动机,考虑自动机的 fail 树,字符串 y 在自动机上表示为一些结点,对于每个结点,假如它能够通过不断地沿 fail 树往上走到达 x 的结束结点,那么 x 就出现了一次。
那么现在问题就变为,在 fail 树中求一个点的子树中某个串的结点一共有几个。
子树查询我们考虑 dfs 序转化为区间查询,那么我们相当于是要询 x 对应的区间里有几个 y
我们离线处理这个问题,并反过来这样想:我们在序列里将 y 对应的结点都标注为 1 ,然后再处理所有有关 y 的询问。
这一过程我们可以 dfs 和树状数组维护。
大意:
给定 n 个模式串,求长度为 m 的至少含有一个模式串的字符串共有多少种
解:
由于是至少一个,直接统计会面临去重的麻烦,因此我们运用补集转化思想,统计一个都不包含的数量。
记 dp[i][j] 为前 i 位对于自动机上结点 j 的方案数,相当于是统计在自动机上走 m 步而不经过结束结点的路径条数
大意:
给定一个 n 个单词的文章,求每个单词在文章中的出现次数
解:
将所有单词建成 AC 自动机,考虑它的 fail 树,我们就是要询问一个单词的结束结点的子树中的所有结点被单词覆盖的次数之和,随便树形 dp 即可
大意:
给定 n 个目标串和 m 个模式串,问这 m 个模式串每个在多少个目标串中出现过,以及 n 个目标串每个以最多多少个模式串为子串
解:
将所有串都扔进 AC 自动机里,考虑 fail 树,我们就是要询问每个模式串的结束结点的子树里的所有结点是多少个不同的目标串的前缀,我们在每个结点上开一个 set ,然后启发式合并即可,第二问合并的时候顺手统计了就可以
大意:
给定 n 个模式串,定义一个字符串的伤害为所有子串的划分中最多包含的模式串数量,求长度为 len 的字符串的伤害期望值
解:
首先建立AC自动机 令 f[i][j] 表示长度为i的字符串在AC自动机上的第 j 个节点的伤害期望值。
如果要走到某个节点是危险节点或者 fail 指针指向危险节点,就 ans++,然后回到根节点 ,容易知道贪心地这样走一定是符合最多包含的要求的。
这时候直接用快速幂加速我们没法统计答案,因此我们开一个计数器,新增一个结点 new ,一旦需要 ans++ 的时候就向 new 转移 1 ,再添加一个 new 的自环,使答案能累计起来
大意:
给定两个字符串集合 S和T ,初始给定 S 集合中的所有字符串,不断向 T 集合中添加字符串,以及询问 S 集合中的某个字符串在 T 集合中的多少个字符串中出现过
解:
假如 T 是不变的,那我们把 S 和 T 都扔进 AC 自动里随便统计一下就行了,而 T 在改变,相当于我们每次把新字符串扔进自动机里跑,跑到的结点塞一个新字符串,然后再询问某个 S 的结束结点的 fail 树中的子树的所有结点的不同字符串数量。
这个问题有很多做法,一种做法是利用树链的并,我们跑到的所有结点直接将它到根的路径上的所有点++,对于很多这样的结点就有很多条路径,我们将这些路径求并(因为同一个串只计算一次)即可。
大意:
给定 n 个长度为 l 的模式串,现在要用前 m 个大写字母生成一个随机串,每个字符有自己的出现几率,第一次出现的字符串获胜,求最终每个字符串的获胜几率
解:
dp 的时候结束结点只转移到自己就行了
大意:
给定一个由 ′a′ 和 ′b′ 构成的字符串,求不连续回文子序列的个数
解:
利用补集转化思想,我们可以求出总回文子序列的个数,然后减掉连续的。
连续的可以用 Manacher 算法解决,不连续的可以用 FFT 解决
大意:
给定一个长度为 n 的 01 串,问有多少个子串满足翻转并取反后和原来一样
解:
定义 0=1,0≠0,1≠1 跑 Manacher 即可
大意:
定义一个回文串的出现值为出现次数*长度,求最大出现值
解:
总之这是一个回文自动机裸题
http://blog.csdn.net/hbhcy98/article/details/51055733
http://blog.csdn.net/hbhcy98/article/details/51056010
http://blog.csdn.net/hbhcy98/article/details/51057065
http://blog.csdn.net/hbhcy98/article/details/51058966
http://blog.csdn.net/hbhcy98/article/details/51088557
http://blog.csdn.net/hbhcy98/article/details/51089554
http://blog.csdn.net/hbhcy98/article/details/51092868
http://blog.csdn.net/hbhcy98/article/details/51093117
http://blog.csdn.net/hbhcy98/article/details/51271987
http://blog.csdn.net/hbhcy98/article/details/51273315