#前言#
之前学了后缀数组,这真是一个神奇的东西。早就想总结一些SA的应用,但一直没时间,现在终于抽出空来写一下自己的心得。
本文只讨论后缀数组的一些应用,不一定全面,仅供参考
还不会后缀数组的同学请自学后再看本文,这里不再赘述后缀数组的基础实现
推荐一篇博客,里面对于基础的后缀数组总结的比较到位,YxuanwKeith大神的五分钟搞懂后缀数组!后缀数组解析以及应用
请不要吐槽标题,不知道你五分钟搞不搞得懂,反正我搞不懂 = = 。
下面进入正题。
#后缀数组相关应用#
##单个字符串相关问题##
比较常见的思路是先构造后缀数组,然后求Height数组,用这两个来求解。
###重复子串问题###
####可重叠的最长重复子串问题####
因为可重复,所以这类问题比较简单。只需要求
H
e
i
g
h
t
Height
Height数组的最大值。因为
H
e
i
g
h
t
[
i
]
Height[i]
Height[i]表示排名为
i
i
i和
i
−
1
i-1
i−1的后缀的LCP,所以这个LCP一定是重复的,只需要求个最大值就是最长的。时间复杂度是线性的。
####不可重叠的最长重复子串问题####
有了不可重叠的限制稍微复杂一点,要用到
H
e
i
g
h
t
Height
Height数组的性质。先二分答案,将问题转化为判定性问题。假设我们二分的长度为
k
k
k,那么答案的两个串它们在SA中的之间的
H
e
i
g
h
t
Height
Height值都会
≥
k
\geq k
≥k(想一想为什么?),所以我们把连续一段
H
e
i
g
h
t
≥
k
Height \geq k
Height≥k的后缀划分成一段,如果有某一段满足段中最大的SA值与最小值之差大于等于
k
k
k,那么当前解就是可行的,因为满足这一条件的串一定不重叠。
注意:这种分段的思想在后缀数组相关问题中很常用
####可重叠的 k 次最长重复子串####
例:(JZOJ2265. 【Usaco DEC06 Gold】Milk Patterns)给定一个字符串,求至少出现 k 次的最长重复子串。
做法和上面的差不多,还是先二分,但是条件改变了,不是不重叠,而是出现至少
k
k
k次。只需判断当前段内是否出现
k
k
k个后缀即可。
###子串计数问题###
####重复出现子串计数问题####
例:(JZOJ1598. 文件修复)求一个字符串中有多少个至少出现两次的子串
这是比较简单的SA题,设每个后缀
R
a
n
k
Rank
Rank为
i
i
i,其最多能贡献
H
e
i
g
h
t
[
i
]
−
H
e
i
g
h
t
[
i
−
1
]
Height[i] - Height[i-1]
Height[i]−Height[i−1]个不同重复子串。
∴
A
n
s
=
∑
m
a
x
(
H
e
i
g
h
t
[
i
]
−
H
e
i
g
h
t
[
i
−
1
]
,
0
)
\therefore Ans = \sum max(Height[i]-Height[i-1],0)
∴Ans=∑max(Height[i]−Height[i−1],0)
####不相同子串计数问题####
例:(spoj694,spoj705)给定一个字符串,求不相同的子串的个数。
和上面思路大相径庭。每个后缀
k
k
k会产生$n-SA[k]+1
个
前
缀
,
但
是
会
重
复
计
数
,
所
以
要
减
去
前
面
相
同
的
前
缀
,
最
后
就
是
个前缀,但是会重复计数,所以要减去前面相同的前缀,最后就是
个前缀,但是会重复计数,所以要减去前面相同的前缀,最后就是n-sa[k]+1- height[k]KaTeX parse error: Expected 'EOF', got '#' at position 6: 个子串。 #̲###字典序第K子串问题###…S
长
度
长度
长度k
,
如
果
,如果
,如果Rank[1]
和
和
和Rank[k+1]
的
的
的Height=|L|-kKaTeX parse error: Expected 'EOF', got '#' at position 11: 那么当前答案合法。 #̲###重复次数最多的连续重复子…L
,
求
重
复
次
数
最
多
的
连
续
重
复
子
串
。
还
是
枚
举
子
串
长
度
,求重复次数最多的连续重复子串。 还是枚举子串长度
,求重复次数最多的连续重复子串。还是枚举子串长度k
,
看
这
个
长
度
对
应
每
个
位
置
(
即
,看这个长度对应每个位置(即
,看这个长度对应每个位置(即L[0],L[k],L[k*2]…
)
之
间
的
L
C
P
是
否
等
于
)之间的LCP是否等于
)之间的LCP是否等于kKaTeX parse error: Expected 'EOF', got '#' at position 19: …扩到哪里,就是重复出现次数。 #̲#多个字符串相关问题## 常用…N
个
字
符
串
,
求
每
个
字
符
串
在
所
有
字
符
串
中
出
现
的
次
数
。
对
于
当
前
字
符
串
个字符串,求每个字符串在所有字符串中出现的次数。 对于当前字符串
个字符串,求每个字符串在所有字符串中出现的次数。对于当前字符串S
,
设
其
在
总
串
中
起
始
位
置
为
,设其在总串中起始位置为
,设其在总串中起始位置为ST[i]
,
那
么
我
们
在
,那么我们在
,那么我们在Height
上
二
分
区
间
上二分区间
上二分区间[1,Rank[ST[i]]],[Rank[ST[i]]+1,n]
内
满
足
内满足
内满足Height\geq|S|KaTeX parse error: Expected 'EOF', got '#' at position 16: ,这之间的后缀数量就是答案。 #̲##字符串子串重复出现k次问题…n
个
操
作
,
每
个
操
作
在
字
符
串
个操作,每个操作在字符串
个操作,每个操作在字符串S$尾插入一个字符,求当前操作后共有多少不同种连续子串。
仔细分析,这题要减去前面的操作对当前影响(即重复的连续子串)。这个应该算是“前缀数组”吧。具体来说就是将字符串翻转后求一边SA,此时所得就是原串的前缀数组。然后在线段树维护一下。
#后记#
这篇博客是在复习之余码的,由于水平有限,难免有疏漏之处,欢迎批评和补充。后续(或许)会有例题及应用的补充。
参考资料:罗穗骞《后缀数组——处理字符串的有力工具》
以上.