前置知识:
(KMP),字典树。
注:本笔记不含任何 (AC) 自动机内容。能用其实现的,我们也用 (KMP) 先做。
例1.
算法一
暴力枚举一个串的所有子串,然后暴力验证。
一个串的子串个数:
暴力验证: (O(100 imes 1000 imes len))
(其中 (len) 为当前验证子串的长度
你会发现,这个算法显然不是很理想。我们考虑基于字符串匹配的某些算法进行优化。
算法二
仍然是枚举一个串的所有约 (5 imes 10^5) 个子串,但是暴力验证需要优化。
我们用 (KMP) 算法, 那么每次验证的时间复杂度就是:
(其中 (len) 为当前验证子串的长度
一个 (1000) 是建立 ( exttt{next}) 数组的时间,另一个 (1000) 是 (KMP) 的时间。
但是,稍作分析你就会发现,总时间复杂度约为:
(O(5 imes 10^5 imes 100 imes (2000 + len) = 5 imes 10^7 imes (2000 + len)))
显然还是不很理想。
算法三
同样是 (KMP) 算法,但是我们仍然有发现:
算法二中,( exttt{next}) 数组不必计算多次,实则可以初始化一次,后面可以一直用。
那么时间复杂度就是:
其中 (len) 为所有子串长度的总和。
但是,你会发现,这个算法仍然不是很理想的样子,还是会 (TLE) 啊!
分析
首先,我们 (len) 从大到小枚举,枚举到一个正确的答案就可以停止,那么很显然不会再枚举下面的答案。
由于串的个数很多,所以 (len) 不会很大,保守的估计在 (100) 左右。
那么,显然不可能每一个 (KMP) 都跑满了,尤其是那些验证子串很大的, 几乎是线性。
通过实践表明,可以跑过。
例2.
有很多单词,只由小写字母组成,不会有重复的单词出现。统计出以某个字符串为前缀的单词数量。(以一个空行隔开单词和前缀字符串)
显然呢,是个字典树模板,不用多说了吧。
例3.
首先呢,你可能想到的是:
(KMP) 暴力匹配,初始化 ( exttt{next}) 数组,瞬间解决!
虽然确实如此 但是我还要弱弱的提醒一下:
也就是说 (KMP) 如果跑满时间复杂度就 (T) 掉了。
但是呢?
你会发现多次 (KMP) 根本就跑不满这个时间复杂度(像上面的那道),因为如果是随机数据,然后你从最长的串开始验证,直接把它 ( exttt{PASS}) 掉就完了,后面的不用再看。
可是无法避免人造数据啊。
比方说, (n=100),然后 (100) 个串都是 (100) 个 ( exttt{a}),每次询问一个长 (1.35 imes 10^6) 的串,它前 (1.35 imes 10^6 - 100) 个都是 ( exttt{b}),后面 (100) 个都是 ( exttt{a}).
这种情况下,虽然每次都是 ( exttt{Yes}),但是你的 (KMP) 成功地跑满了时间复杂度然后就 (T) 掉了。
可是多次思考后我们发现,似乎没有比这更优的算法?(有当我没说)
所以就不管它,直接硬跑就完事了。((T) 了不管我事)