<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Strings |</title><link>https://sichen-wang.github.io/tags/strings/</link><atom:link href="https://sichen-wang.github.io/tags/strings/index.xml" rel="self" type="application/rss+xml"/><description>Strings</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sat, 24 Jun 2023 11:00:56 +0000</lastBuildDate><image><url>https://sichen-wang.github.io/media/icon_hu_9dfcb674aa885f38.png</url><title>Strings</title><link>https://sichen-wang.github.io/tags/strings/</link></image><item><title>(可持久化) Trie 树</title><link>https://sichen-wang.github.io/blog/%E5%8F%AF%E6%8C%81%E4%B9%85%E5%8C%96-trie-%E6%A0%91/</link><pubDate>Sat, 24 Jun 2023 11:00:56 +0000</pubDate><guid>https://sichen-wang.github.io/blog/%E5%8F%AF%E6%8C%81%E4%B9%85%E5%8C%96-trie-%E6%A0%91/</guid><description>&lt;h2 id="字典树"&gt;字典树&lt;/h2&gt;
&lt;p&gt;顾名思义，字典树就是支持像字典一样加入和查询字符串的树形数据结构，实现如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;Trie&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;K&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Insert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sc"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Find&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="sc"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="01-trie"&gt;0/1 Trie&lt;/h2&gt;
&lt;p&gt;在很多问题中，字符串仅由 \(0\) 和 \(1\) 构成，这些问题存在一定的共性。&lt;/p&gt;
&lt;h3 id="维护异或极值"&gt;维护异或极值&lt;/h3&gt;
&lt;p&gt;考虑如下问题，给定一颗带边权的树，求 \(\max\limits_{i,j\in[1,n]} \oplus Path(i,j)\)。&lt;/p&gt;
&lt;p&gt;由异或的性质，可知 \(\oplus Path(i,j)=\oplus Path(root,i)\oplus Path(root,j)\)。&lt;/p&gt;
&lt;p&gt;所以可以把所有从根开始的路径的异或和表示成一个数字，这样就将问题转化成了求任意两个数字的异或的最大值，可以用 \(\rm 0/1\ Trie\) 解决。&lt;/p&gt;
&lt;h3 id="维护异或和"&gt;维护异或和&lt;/h3&gt;
&lt;p&gt;考虑如下问题，给一些数，维护它们的异或和，支持插入，删除和全局 \(+1\)。&lt;/p&gt;
&lt;p&gt;对于异或和的维护，发现 \(sum=\sum_{i=0}^{k}2^{i}\times [num_{i}\equiv 1\pmod 2]\)。&lt;/p&gt;
&lt;p&gt;其中 \(sum\) 表示异或和，\(num_{i}\) 表示所有数中二进制的第 \(i\) 位为 \(1\) 的数量。这个式子体现在 \(\rm 0/1\ Trie\) 上就是对于所有以父亲边为 \(1\) 的结点为根的子树中有多少个 \(\rm Tag\)，令 \(\rm Tag\) 的数量为 \(num\)，这些结点的深度为 \(k\)，它们对答案的贡献即为 \(num\%2\times 2^{k}\)。&lt;/p&gt;
&lt;p&gt;对于实现全局 \(+1\) 操作，从低位到高维建 \(\rm 0/1\ Trie\)，考虑将一个二进制数 \(+1\) 的一般操作，即找到从低位到高位的第一个 \(0\)，将它变成 \(1\)，数位比它低的 \(1\) 全部变成 \(0\)。 而这个做法体现在 \(\rm 0/1\ Trie\) 上可以将一个结点的两条指向儿子的出边交换，再递归向交换后的 \(0\) 边迭代操作。&lt;/p&gt;
&lt;h3 id="01-trie-合并"&gt;0/1 Trie 合并&lt;/h3&gt;
&lt;p&gt;与线段树合并类似，实现如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Merge&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="n"&gt;Merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Push_up&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;需要注意的是，Trie 树合并和线段树合并一样，只有当每次合并的集合没有交集的时候复杂度才是 \(\Theta(n\log n)\) 的。&lt;/p&gt;
&lt;h2 id="可持久化-trie-树"&gt;可持久化 Trie 树&lt;/h2&gt;
&lt;p&gt;可持久化 Trie 的实现方式和可持久化线段树的方式是相似的，即每次只修改被添加或值被修改的节点，而保留没有被改动的节点，在上一个版本的基础上连边，使最后每个版本的 Trie 树的根遍历所能分离出的 Trie 树都是完整且包含全部信息的。实现如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;Trie&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Build&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;Query&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="经典例题"&gt;经典例题&lt;/h2&gt;
&lt;h3 id="lg4735-最大异或和"&gt;[LG4735] 最大异或和&lt;/h3&gt;
&lt;p&gt;可持久化 \(0/1\) Trie 模板题。&lt;/p&gt;
&lt;p&gt;考虑记所有的前缀和，答案变为统计 \(x\oplus sum_{n}\) 在 \([l-1,r-1]\) 这个区间的 \(0/1\) Trie 中的最大异或值。&lt;/p&gt;
&lt;h3 id="cf455b-a-lot-of-games"&gt;[CF455B] A Lot of Games&lt;/h3&gt;
&lt;p&gt;显然直接建立 Trie 树，每次相当于向 Trie 树的子树内走一步，不能走者为负。&lt;/p&gt;
&lt;p&gt;考虑设 \(f\) 表示是否存在必胜策略，\(g\) 表示是否存在必败策略，转移显然。&lt;/p&gt;
&lt;p&gt;对 \(f_{root}\) 和 \(g_{root}\) 分四种情况讨论，每种情况下的答案易得。需要注意的是，如果先手既存在必胜策略又存在必败策略，那他可以故意输以获得下一局的先手，然后在最后一局获得胜利。&lt;/p&gt;
&lt;h3 id="cf888g-xor-mst"&gt;[CF888G] Xor-MST&lt;/h3&gt;
&lt;p&gt;\(\rm Trie+Boruvka\)。&lt;/p&gt;
&lt;p&gt;考虑将所有树插入一个 Trie 中，枚举到某个连通块时将其中所有点从 Tire 中删除，再用连通块中每个结点从 Trie 中查找出最小边权，完成后再将连通块中的点权加回去。&lt;/p&gt;
&lt;p&gt;发现一个性质，每次合并的连通块都是 Trie 中某个节点的左右子树，再由 Trie 的性质，我们甚至不需要把 Trie 建出来，只需要将 A 排序，二分 Trie 的左右儿子，再将其中一个儿子的所有结点建 Trie，用另一个儿子中的所有结点在其中查找连通块之间的最小边权即可。&lt;/p&gt;
&lt;p&gt;由于每个结点最多被加进 Trie 树 \(\log n\) 次，每次复杂度为 \(\log W\)，所以总时间复杂度为 \(\Theta(n\log n\log W)\)。&lt;/p&gt;</description></item><item><title>AC 自动机</title><link>https://sichen-wang.github.io/blog/ac-%E8%87%AA%E5%8A%A8%E6%9C%BA/</link><pubDate>Sat, 24 Jun 2023 11:00:56 +0000</pubDate><guid>https://sichen-wang.github.io/blog/ac-%E8%87%AA%E5%8A%A8%E6%9C%BA/</guid><description>&lt;h3 id="失配指针"&gt;失配指针&lt;/h3&gt;
&lt;p&gt;定义 fail 为广义的 \(\pi\) 函数，即对于 \(n\) 个字符串，\(fail_{i,j}=\max\limits_{k=1}^{n} \pi(S_{k}\#S_{i,1\sim j})\)。&lt;/p&gt;
&lt;p&gt;上面那个是一种定义，但事实上，在 \(\rm AC\) 自动机中，fail 是具有指向性的，它指向的是所有模式串中，前缀与当前字符串某个前缀的后缀匹配可以产生最大匹配的那个串，它与当前串匹配的最后一个结点。&lt;/p&gt;
&lt;h3 id="构建-ac-自动机"&gt;构建 AC 自动机&lt;/h3&gt;
&lt;p&gt;首先对所有模式串建立 Trie 树，显然 Trie 树上的每一个结点都对应某个模式串的某个前缀。&lt;/p&gt;
&lt;p&gt;然后将每个 fail 都指向匹配的结点，显而易见，所有的 fail 都指向一个祖先节点，这个性质后面要用到。&lt;/p&gt;
&lt;p&gt;最后构建转移边 Auto，Auto 指向跳 fail 过程中存在字符 \(c\) 的结点。&lt;/p&gt;
&lt;p&gt;具体来说，可以以 \(\rm BFS\) 序遍历 Trie 树，在遍历的同时更新答案，转移式如下：&lt;/p&gt;
\[
\begin{cases}
fail_u = Auto_{fail_{fa_u}, c} \\
Auto_{u, c} = Auto_{fail_u, c}
\end{cases}
\]&lt;p&gt;建 \(\rm AC\) 自动机复杂度为 \(\Theta(|\Sigma|n)\)。&lt;/p&gt;
&lt;h3 id="文本串匹配"&gt;文本串匹配&lt;/h3&gt;
&lt;p&gt;遍历文本串，直接按照转移边走即可，如果要统计每个模式串出现的次数，就将所有经过的点权值 \(+1\)，再在 fail 树上差分即可计算。&lt;/p&gt;
&lt;p&gt;令模式串为 \(S\)，文本串为 \(T\)，总时间复杂度为 \(\Theta(|\Sigma||S|+|T|)\)。&lt;/p&gt;
&lt;p&gt;例题：
&lt;/p&gt;
&lt;p&gt;首先有一个显然的 \(\Theta(|s|\sum|t|)\) 暴力，枚举文本串的转移点，显然其不能超过最大串长，转移式为：&lt;/p&gt;
\[
f_i=\bigvee_{j=i-|s|}^{i-1}\{f_j\wedge[t_{j+1\sim i}\in s]\}
\]&lt;p&gt;这个式子的核心在于，\(f_{i}=true\) 当且仅当它存在一个后缀，这个后缀是某个模式串，并且删去这个后缀以后的文本串仍合法。&lt;/p&gt;
&lt;p&gt;于是考虑建立 \(\rm AC\) 自动机，将文本串放在自动机上跑的时候记 \(f_{i}\) 表示当前前缀是否合法。&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;存在如下 \(\rm AC\) 自动机性质：如果 \(v\) 可以从 \(u\) 开始由跳 fail 得到，即 fail 树上 \(v\) 是 \(u\) 的祖先，那么 \(v\) 代表的字符串是 \(u\) 代表的字符串的一个后缀。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以从 \(u\) 开始遍历跳 fail 经过的结点，如果它代表的字符串长度为 \(l\)，且为某个模式串的结尾，则将 \(g_{u,l}\) 记为 true。 由于 \(|s|\leqslant 10\)，所以只有当 \(l\leqslant 10\) 的时候 \(g_{u,l}\) 可能为 true，故直接状压成 \(g_{u}\)，其二进制下第 \(l\) 位表示之前 \(g_{u,l}\) 的值。&lt;/p&gt;
&lt;p&gt;最后直接将文本串放在 \(\rm AC\) 自动机上跑即可，记 \(now_{i}\) 表示文本串中当前点前 \(i\) 位的 \(f\) 值为多少，易知 \(i\leqslant 10\)，故也可以状压成一个数 \(now\)。 易知 \(f_{i}=true\) 当且仅当 \([now \operatorname{or} g_{u}]=true\)。&lt;/p&gt;
&lt;p&gt;总时间复杂度 \(\Theta(|\Sigma|n|s|^{2}+m|t|)\)。&lt;/p&gt;</description></item><item><title>KMP 算法 &amp; KMP 自动机</title><link>https://sichen-wang.github.io/blog/kmp-%E7%AE%97%E6%B3%95-kmp-%E8%87%AA%E5%8A%A8%E6%9C%BA/</link><pubDate>Sat, 24 Jun 2023 11:00:56 +0000</pubDate><guid>https://sichen-wang.github.io/blog/kmp-%E7%AE%97%E6%B3%95-kmp-%E8%87%AA%E5%8A%A8%E6%9C%BA/</guid><description>&lt;h2 id="border--pi-函数"&gt;Border &amp;amp; pi 函数&lt;/h2&gt;
&lt;h3 id="一些定义"&gt;一些定义&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;字符串 \(S\) 的真前/后缀为非自身的前/后缀。&lt;/li&gt;
&lt;li&gt;字符串 \(S\) 的 border为 \(S\) 的公共真前/后缀。&lt;/li&gt;
&lt;li&gt;字符串 \(S\) 的最长 border 为 \(\pi\)，对于 \(S\) 的每个前缀 \(S_{1 \sim i}\) 令 \(\pi_{i}\) 为其最长 border，\(\pi\) 函数就是所谓的前缀函数。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="pi-函数的性质"&gt;pi 函数的性质&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;\(\forall i,\pi_{i+1}\leqslant \pi_{i}+1\)。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;由反证法易知。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;字符串 \(S\) 的所有 border 可以由 \(\pi_{|S|}\) 开始不断跳 \(\pi\) 由长度从大到小遍历。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;只需证明 \(S_{1 \sim \pi_{\pi_{n}}}\) 是 \(S\) 的次长 border 即可。&lt;/p&gt;
&lt;p&gt;由 \(\pi\) 的定义易知。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;相邻前缀函数满足 \(\pi_{i + 1} = \max\limits_{T \ is \ the \ border \ of \ S_{1 \sim i}, S_{i + 1} = S_{|T| + 1}} |T| + 1\)。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;使用反证法易知。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="pi-函数的线性求法"&gt;pi 函数的线性求法&lt;/h3&gt;
&lt;p&gt;有了上述几条性质，我们可以得到一个求解 \(\pi\) 函数的算法：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;直观感受上这个做法是 \(\Theta(n^{2})\) 的，但实际上它是线性的，复杂度证明如下：&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;不难发现复杂度来源在于跳 \(\pi\)，下面我们将证明跳 \(\pi\) 的总次数是线性的。&lt;/p&gt;
&lt;p&gt;注意 \(\pi_{i}\) 的位置变化情况，不难发现 \(\pi_{i}\) 只会每次 \(+1\) 或往前跳若干次，但往前跳的的总距离不能超过当前 \(+1\) 的次数，故总路程也是线性的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="pi-函数的基本应用"&gt;pi 函数的基本应用&lt;/h3&gt;
&lt;h4 id="字符串匹配"&gt;字符串匹配&lt;/h4&gt;
&lt;p&gt;求解模式串 \(T\) 在匹配串 \(S\) 中的出现位置的问题。&lt;/p&gt;
&lt;p&gt;构造一个新的字符串 \(T\circ S\)，其中 \(\circ\not\in S\cup T\)，它仅作为一个分隔符。&lt;/p&gt;
&lt;p&gt;对这个新的字符串求其 \(\pi\) 函数，不难发现由于分隔符的出现，\(\forall i, \pi_{i} \leqslant |T|\)，于是找到 \(\pi_{i} = |T|\) 的位置，\(i - |T| + 1 \sim i\) 就是一个匹配。&lt;/p&gt;
&lt;p&gt;以上即为 \(\rm KMP\) 算法。&lt;/p&gt;
&lt;h4 id="统计每个前缀的出现次数"&gt;统计每个前缀的出现次数&lt;/h4&gt;
&lt;p&gt;考虑到如果某个前缀在之后的位置里出现过，那么一定是某个前缀的 border，于是我们考虑在每个数的最大 border 处统计答案，其为更小 border 的重复次数，并且更小的 border 一定可以通过跳 \(\pi\) 遍历到，这样只需要从后往前扫一遍，复杂度 \(\Theta(n)\)。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ans&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;ans&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pi&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;ans&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;至于这个问题的加强版，统计 \(S\) 中的每个前缀在 \(T\) 中的出现次数，可以构造 \(S\#T\)，赋初值的时候只需要将 \(T\) 中的位置初始化为 \(1\) 即可。&lt;/p&gt;
&lt;h4 id="统计本质不同子串的数目"&gt;统计本质不同子串的数目&lt;/h4&gt;
&lt;p&gt;考虑迭代计算，问题转化成：在当前字符串的末尾加入一个新的字符，以这个字符结尾的后缀中有多少个是前面没有出现过的。&lt;/p&gt;
&lt;p&gt;如果一个子串 \(S_{i\sim n}\) 在前面出现过，则 \(\forall j\geqslant i,S_{j\sim n}\) 都在前面出现过。&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;由反证法易知。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;于是就只需求出加入新字符后的串的反串中的 \(\max\pi\) 即可，而这个字符的贡献就是 \(|S|+1-\max\pi\)，时间复杂度 \(\Theta(n^{2})\)。&lt;/p&gt;
&lt;h2 id="字符串周期相关性质"&gt;字符串周期相关性质&lt;/h2&gt;
&lt;p&gt;称 \(k\) 为 \(S\) 的一个周期且仅当 \(\forall i\leqslant |S|-k, S_{i} = S_{i + k}\)，特别地若 \(k \nmid S\) 则称 \(k\) 为 \(S\) 的弱周期。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若字符串 \(S\) 存在一个 border \(T\)，当且仅当 \(S\) 存在一个长度为 \(|S| - |T|\) 的（弱）周期。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;必要性显然，充分性递归论证即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;字符串 \(S\) 的最短（弱）周期长度为 \(n - \pi_{n}\)。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;由 \(\pi\) 函数的定义易知。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;若长度不小于 \(p + q\) 的字符串 \(S\) 存在长度分别为 \(p, q\) 的（弱）周期，那么 \(\gcd(p, q)\) 也是 \(S\) 的一个（弱）周期。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;下面首先证明 \(p - q(p \gt q)\) 也是 \(S\) 的一个（弱）周期。&lt;/p&gt;
&lt;p&gt;分两种情况讨论：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;若 \(i \leqslant q\)，则 \(S_{i} = S_{i + p} = S_{i + p - q}\)，可知 \(\forall i \leqslant q, p - q\) 是其一个（弱）周期。&lt;/li&gt;
&lt;li&gt;若 \(i \gt q\)，则 \(S_{i} = S_{i - q} = S_{i + p - q}\)，可知 \(\forall i \gt q, p - q\) 是其一个（弱）周期。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;证明 \(p - q\) 是 \(S\) 的一个（弱）周期后，根据更相减损术的性质，最终可以迭代至 \(\gcd(p, q)\) 是 \(S\) 的一个（弱）周期。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;所有强周期的长度均为最短强周期长度的倍数。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;若存在一个强周期 \(x\)，显然有 \(x\mid n\)，若 \(n-\pi_{n}\nmid x\)，则 \(\gcd(n-\pi_{n},x)\lt n-\pi_{n}\) 也为一个强周期，与 \(n-\pi_{n}\) 是最小强周期矛盾。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;字符串 \(S\) 存在周期当且仅当 \(n - \pi_{n}\) 为一个周期。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;必要性显然。若存在强周期 \(x\mid n\)，又因为 \(n-\pi_{n}\mid x\)，则 \(n-\pi_{n}\mid n\)，充分性得证。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="kmp-自动机"&gt;KMP 自动机&lt;/h2&gt;
&lt;p&gt;在求 \(\pi\) 函数的时候，我们发现这个做法是支持在线插入一个字符在末尾并计算函数值的。&lt;/p&gt;
&lt;p&gt;并且在做 \(\rm KMP\) 的过程中我们发现，在匹配串每一位求 \(\pi\) 函数时，并不关心之前匹配串的字符是什么，只关心之前的 \(\pi\) 函数值和当前位的字符。&lt;/p&gt;
&lt;p&gt;这意味着可以对一个模式串建立一个自动机，结点 \(i\) 表示当前节点的 \(\pi\) 值为 \(i\)，新加入一个节点的出边 \(c\) 指向跳 \(\pi\) 时第一个能够匹配的结点。暴力建这个自动机的复杂度为 \(\Theta(n^{2}|\Sigma|)\)，因为出边需要遍历 \(\Sigma\)，所以不存在求 \(\pi\) 函数时指针单方向移动的性质。&lt;/p&gt;
&lt;p&gt;但是实际上之前已经计算了从所有节点开始，以任意一个字符为出边会到哪个节点，故可以直接调用。具体规则为，记 \(f_{i,c}\) 表示从 \(i\) 号节点开始，字符 \(c\) 会到达的节点。如果当前到达了节点 \(i\) ，字符为 \(c\)，则如果 \(c=s_{i+1}\)，那么当前节点指向 \(i+1\)，否则指向 \(f_{\pi_{i},c}\)。时间复杂度 \(\Theta(n|\Sigma|)\)。&lt;/p&gt;
&lt;p&gt;同 \(\rm KMP\) 算法一样，\(\rm KMP\) 自动机也可以用来实现若干文本串和模式串的匹配，文本串从节点 \(0\) 开始走，每次走到 \(|S|\) 都完成了一次匹配。若模式串长度为 \(m\)，文本串长度为 \(n\)，数量为 \(t\)，则时间复杂度为 \(\Theta(m+tn|\Sigma|)\)。&lt;/p&gt;
&lt;p&gt;另一方面，\(\rm KMP\) 自动机在完成普通字符串匹配时表现并不优秀，但它可以求解一些特殊的字符串匹配问题，如：&lt;/p&gt;
&lt;p&gt;定义 \(g_{1}="a"\)，\(g_{2}="aba"\)，\(g_{3}="abacaba"\cdots \forall i,g_{i}=g_{i-1}+(char)i+g_{i-1}\)。 给定另一字符串 \(S\) 和整数 \(k\)，求 \(S\) 在 \(g_{k}\) 中的匹配次数。保证 \(|S|\times k\leqslant 10^{6}\)。&lt;/p&gt;
&lt;p&gt;显然题目是要求从 \(0\) 号节点开始对 \(g_{k}\) 进行匹配，经过 \(|S|\) 号节点的次数是多少。&lt;/p&gt;
&lt;p&gt;首先对 \(S\) 建立 \(\rm KMP\) 自动机，记 \(f_{i,j}\) 表示从 \(i\) 号节点开始对 \(g_{j}\) 进行匹配后经过 \(|S|\) 号节点的次数。记 \(k_{i,S}\) 表示从 \(i\) 号节点开始，匹配完字符串 \(S=g_{i}+(char)i\) 后停下来的结点，那么有 ：&lt;/p&gt;
\[
f_{i,j}=f_{i,j-1}+[k_{i,g_{j-1}+(char)(j-1)}=|S|]+f_{k_{i,g_{j-1}+(char)(j-1)},j-1}
\]&lt;p&gt;总时间复杂度 \(\Theta(|S|\times k)\)。&lt;/p&gt;
&lt;p&gt;另外，由于单次添加结点的复杂度是严格 \(\Theta(|\Sigma|)\) 的，所以可以对 \(\rm KMP\) 自动机进行可持久化处理，总时间复杂度 \(\Theta(n|\Sigma|\log n)\)。&lt;/p&gt;
&lt;p&gt;例题：
&lt;/p&gt;
&lt;p&gt;考虑对模式串建立 \(\rm KMP\) 自动机。&lt;/p&gt;
&lt;p&gt;令 \(f_{i,j}\) 表示对字符串匹配到第 \(i\) 位时，跳到自动机的 \(j\) 号点匹配的次数最大值，显然有转移：&lt;/p&gt;
\[
f_{i, to} =
\begin{cases}
\max\{f_{i-1, j} + [to = m]\} &amp; (s_i \not= '?') \\
\max\limits_{k = 0}^{25}\{f_{i-1, j} + [to = m]\} &amp; (s_i = '?')
\end{cases}
\]&lt;p&gt;由于 \(\rm DP\) 空间开不下，需要使用滚动数组。&lt;/p&gt;
&lt;p&gt;时间复杂度 \(\Theta(|S||T||\Sigma|)\)。&lt;/p&gt;</description></item><item><title>Manacher 算法</title><link>https://sichen-wang.github.io/blog/manacher-%E7%AE%97%E6%B3%95/</link><pubDate>Sat, 24 Jun 2023 11:00:56 +0000</pubDate><guid>https://sichen-wang.github.io/blog/manacher-%E7%AE%97%E6%B3%95/</guid><description>&lt;p&gt;\(\rm Manacher\) 是用于求解一个字符串中所有回文子串的算法。&lt;/p&gt;
&lt;p&gt;首先，回文子串的数量级是 \(n^{2}\) 的，所以要考虑如何将所有回文子串快速地表达出来，容易发现如下性质：&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;如果存在一个以 \(p\) 为中心，半径为 \(d\) 的回文子串，那所有以 \(p\) 为中心，半径小于 \(d\) 的子串都是回文子串，证明显然。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;于是我们记 \(d_{i}\) 表示以 \(i\) 为中心的最大回文子串，而 \(\rm Manacher\) 算法的用处就是在线性复杂度下求出这个序列。&lt;/p&gt;
&lt;p&gt;具体过程跟 \(Z\) 算法类似，记 \(L,R\) 表示在之前的回文子串中，右端点最靠右的子串的左右端点，转移分三种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若 \(i\gt R\)，令 \(L=i-1,R=i+1\)，暴力更新。&lt;/li&gt;
&lt;li&gt;若 \(i\leqslant R,\ d_{L+R-i}\lt R-i\)，\(d_{i}=d_{L+R-i}\)。&lt;/li&gt;
&lt;li&gt;若 \(i\leqslant R,\ d_{L+R-i}\geqslant R-i\)，令 \(L=2i-R\)，暴力更新。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;由于 \(R\) 只会往右移，且每次暴力更新必定右移一位，所以时间复杂度为 \(\Theta(n)\)。&lt;/p&gt;
&lt;p&gt;但是我们发现，上面这种方法只处理了回文子串长度为奇数的情况，而偶数的情况只需要分开考虑，奇偶性稍微改变一下即可，但还有一种更加巧妙的方式可以规避分奇偶讨论。&lt;/p&gt;
&lt;p&gt;考虑给所有字符之间加入一个不可能出现的相同字符，如字符串 \(abaca\) 变成 \(\#a\#b\#a\#c\#a\#\)，这样在奇数位上统计的就是字串长度为偶数的答案，在偶数位上统计的就是字串长度为奇数的答案。同时，这样统计出来的半径 \(d\) 就是以当前点为中心的最长回文子串的长度。&lt;/p&gt;</description></item><item><title>Z 算法</title><link>https://sichen-wang.github.io/blog/z-%E7%AE%97%E6%B3%95/</link><pubDate>Sat, 24 Jun 2023 11:00:56 +0000</pubDate><guid>https://sichen-wang.github.io/blog/z-%E7%AE%97%E6%B3%95/</guid><description>&lt;h2 id="算法简介"&gt;算法简介&lt;/h2&gt;
&lt;p&gt;\(Z\) 算法在国内也被某些人称为 \(\rm ExKMP\)（虽然我不知道它跟 \(\rm KMP\) 算法有什么关系）。&lt;/p&gt;
&lt;p&gt;\(Z\) 算法用于求字符串 \(S\) 的 \(Z\) 函数，其定义为 \(z_{i}=\rm LCP(S,S_{i\sim n})\)。&lt;/p&gt;
&lt;p&gt;这个算法的核心在于利用之前的信息来减少计算量。&lt;/p&gt;
&lt;p&gt;具体来说我们可以从左往右依次求每个位置的 \(z\) 函数，令 \([i, i + z_{i} - 1]\) 为 \(i\) 的匹配段，同时维护出右端点最靠右的匹配段 \([l, r]\)。&lt;/p&gt;
&lt;p&gt;那么我们知道有 \(S_{1 \sim r - l + 1} = S_{l \sim r}, S_{i \sim r} = S_{i - l + 1, r - l + 1}(i \leqslant r), z_{i} \geqslant \min(r - i + 1, z_{i - l + 1})\)&lt;/p&gt;
&lt;p&gt;在计算 \(z_{i}\) 时，分以下三种情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若 \(i \leqslant r, z_{i - l + 1} \lt r - i + 1\) 那么显然有 \(z_{i} = z_{i - l + 1}\)。&lt;/li&gt;
&lt;li&gt;若 \(i \leqslant r, z_{i - l + 1} \geqslant r - i + 1\)，那么我们就从 \(r + 1\) 开始暴力匹配前缀。&lt;/li&gt;
&lt;li&gt;若 \(i \gt r\)，那么直接从 \(i\) 开始暴力匹配前缀。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不难发现复杂度来源在于 \(2, 3\) 情况，但可以发现右端点最靠右的匹配段的右端点总是在这两种情况下向右移动，而总移动量不超过 \(n\)，因此复杂度是 \(\Theta(n)\) 的。&lt;/p&gt;
&lt;p&gt;如果要求 \(z_{i} = \rm LCP(T, S_{i \sim n})\) 话，就类似于 \(\rm KMP\) 的做法，构造一个 \(T \# S\) 的串去求 \(z\) 函数即可。&lt;/p&gt;
&lt;h2 id="经典例题"&gt;经典例题&lt;/h2&gt;
&lt;h3 id="noi2014-动物园"&gt;[NOI2014] 动物园&lt;/h3&gt;
&lt;p&gt;对于每个 \(i\)，计算后缀 \(S_{i\sim n}\) 与 \(S\) 的 \(\rm LCP\)，考虑在后一个 border 的左端点 \(i\) 处统计答案。&lt;/p&gt;
&lt;p&gt;令最长公共前缀的四个端点分别为 \(L,R,L',R'\)，分两种情况讨论：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当 \(R\lt L'\) 时，区间 \([L',R']\) 答案 \(+1\)。&lt;/li&gt;
&lt;li&gt;当 \(R\geqslant L'\) 时，区间 \([L',R]\) 答案 \(+1\)。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;区间 \(+1\) 可以使用差分实现，总时间复杂度 \(\Theta(n)\)。&lt;/p&gt;
&lt;h3 id="cf1051e-vasya-and-big-integers"&gt;[CF1051E] Vasya and Big Integers&lt;/h3&gt;
&lt;p&gt;记 \(f_{i}\) 表示 \(a_{1\sim i}\) 有多少种合法的划分方案，显然有一个 \(\Theta(n^{2})\) 的暴力 \(\rm DP\)，即往前枚举转移点 \(j\)，判断转移是否合法后累加。&lt;/p&gt;
&lt;p&gt;观察到合法的转移点是一段连续的区间，若令 \(len_{l}\) 和 \(len_{r}\) 分别表示两限制字符串的长度，显然对于满足 \(len_{l}\lt i-j\lt len_{r}\) 的转移点 \(j\)，这个转移都是合法的，现在的问题在于判断当 \(j=i-len_{l}\) 或 \(j=i-len_{r}\) 的时候转移是否合法。&lt;/p&gt;
&lt;p&gt;考虑用 \(\rm ExKMP\) 对任意一个 \(i\)，求 \(\rm LCP(s_{i\sim n},l)\) 和 \(\rm LCP(s_{i\sim n},r)\)，这样就可以快速求出 \(s_{i-len_{l}+1\sim i}\) 与 \(l\) 的最长公共前缀 \(zl_{i-len_{l}+1}\)，并通过比较 \(s_{i-len_{l}+1+z_{i-len_{l}+1}}\) 与 \(l_{z_{i-len_{l}+1}+1}\) 的大小判断当 \(j=i-len_{l}\) 时转移是否合法，同理，当 \(j=i-len_{r}\) 时转移是否合法也可以通过相同方式判断。&lt;/p&gt;
&lt;p&gt;这样，合法转移点的区间就在 \(\Theta(1)\) 的时间内求出来了，剩下的用前缀和记录一下即可。需要注意的是，由于不能出现前导零，所以当转移点的后一位为 ‘\(0\)’ 时不能将其加入前缀和数组。&lt;/p&gt;
&lt;p&gt;总时间复杂度 \(\Theta(n)\)。&lt;/p&gt;</description></item></channel></rss>