前置知识:

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=11000=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×106100 个都是 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 了不管我事)