字符串笔记

前置知识:

\(KMP\),字典树。

注:本笔记不含任何 \(AC\) 自动机内容。能用其实现的,我们也用 \(KMP\) 先做。

例1.

算法一

暴力枚举一个串的所有子串,然后暴力验证。

一个串的子串个数:

\[\sum_{i=1}^{1000} = 500500 \]

暴力验证: \(O(100 \times 1000 \times len)\)

(其中 \(len\) 为当前验证子串的长度

你会发现,这个算法显然不是很理想。我们考虑基于字符串匹配的某些算法进行优化。

算法二

仍然是枚举一个串的所有约 \(5 \times 10^5\) 个子串,但是暴力验证需要优化。

我们用 \(KMP\) 算法, 那么每次验证的时间复杂度就是:

\[O(100 \times (1000 + 1000 + len)) \]

(其中 \(len\) 为当前验证子串的长度

一个 \(1000\) 是建立 \(\texttt{next}\) 数组的时间,另一个 \(1000\)\(KMP\) 的时间。

但是,稍作分析你就会发现,总时间复杂度约为:

\(O(5 \times 10^5 \times 100 \times (2000 + len) = 5 \times 10^7 \times (2000 + len))\)

显然还是不很理想。

算法三

同样是 \(KMP\) 算法,但是我们仍然有发现:

算法二中,\(\texttt{next}\) 数组不必计算多次,实则可以初始化一次,后面可以一直用。

那么时间复杂度就是:

\[O(5 \times 10^5 \times 100 \times len) \]

其中 \(len\) 为所有子串长度的总和。

但是,你会发现,这个算法仍然不是很理想的样子,还是会 \(TLE\) 啊!

分析

首先,我们 \(len\) 从大到小枚举,枚举到一个正确的答案就可以停止,那么很显然不会再枚举下面的答案。

由于串的个数很多,所以 \(len\) 不会很大,保守的估计在 \(100\) 左右。

那么,显然不可能每一个 \(KMP\) 都跑满了,尤其是那些验证子串很大的, 几乎是线性。

通过实践表明,可以跑过。

例2.

   有很多单词,只由小写字母组成,不会有重复的单词出现。统计出以某个字符串为前缀的单词数量。(以一个空行隔开单词和前缀字符串)

显然呢,是个字典树模板,不用多说了吧。

例3.

首先呢,你可能想到的是:

\(KMP\) 暴力匹配,初始化 \(\texttt{next}\) 数组,瞬间解决!

虽然确实如此 但是我还要弱弱的提醒一下:

\[100 \times 1350000 > 10^8 \]

也就是说 \(KMP\) 如果跑满时间复杂度就 \(T\) 掉了。

但是呢?

你会发现多次 \(KMP\) 根本就跑不满这个时间复杂度(像上面的那道),因为如果是随机数据,然后你从最长的串开始验证,直接把它 \(\texttt{PASS}\) 掉就完了,后面的不用再看。

可是无法避免人造数据啊。

比方说, \(n=100\),然后 \(100\) 个串都是 \(100\)\(\texttt{a}\),每次询问一个长 \(1.35 \times 10^6\) 的串,它前 \(1.35 \times 10^6 - 100\) 个都是 \(\texttt{b}\),后面 \(100\) 个都是 \(\texttt{a}\).

这种情况下,虽然每次都是 \(\texttt{Yes}\),但是你的 \(KMP\) 成功地跑满了时间复杂度然后就 \(T\) 掉了。

可是多次思考后我们发现,似乎没有比这更优的算法?(有当我没说)

所以就不管它,直接硬跑就完事了。(\(T\) 了不管我事)

posted @ 2020-03-15 17:58  bifanwen  阅读(145)  评论(0编辑  收藏  举报