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