最小循环节——题解

最小循环节

题目链接

题意简述

我们需要找到一个字符 \(s\) 的最短的循环节,对循环节的定义为,当一个字符串 \(t\) 能够通过若干次自己加自己得到字符串 \(s\) ,我们就称 \(t\)\(s\) 的一个循环节。

思路简述

根据题意,字符串 \(s\) 本身就是自己的一个循环节。所以,当我们找不到更小的循环节时,我们就可以输出 \(n\)

接下来,我们就要找到比 \(s\) 本身更短的循环节,考虑 \(KMP\) 中对 \(next\) 数组的定义,它记录了一个字符串每一个前缀的 \(border\) ,我们会惊奇的发现,这个定义与循环节的定义有异曲同工之妙。

假设 \(s\) 的最短循环节的长度,就是 \(next_{|s|}\) ,这貌似是对的,但我们仍能举出反例,如:

\[ abababab \to next_{|s|} \to 6 \to error \]

这个样例的循环节应该是 \(ab\) ,长度应该是 \(2\)

我们继续观察对 \(next\) 数组的定义,发现,他记录的是每一个前缀的 \(border\) 是关于真前缀与真后缀的,即不可能是这个前缀本身。所以,我们就可以考虑 \(n-next_{|s|}\) ,考虑 \(next\) 数组的定义, \(n-next_{|s|} \ge 1\) ,但是就算是这样,\(s\) 的最小循环节也不一定就是它。

举个栗子:

\[abcab \to 最小循环节 \to 5 \]

因为循环节的定义——当一个字符串 \(t\) 能够通过若干次自己加自己得到字符串 \(s\) ,我们就称 \(t\)\(s\) 的一个循环节。
我们可以发现,

\[|t|\ \ 整除\ \ |s| \]

所以我们只需要判断 \(n-next_{|s|}\) 能否整除 \(n\) 即可。

如果能,那么 \(n-next_{|s|}\) 就是 \(s\) 的最小循环节,否则,\(s\) 的最小循环节就是他本身。

那么,它的正确性如何证明呢?

证明正确

因为 \(next\) 数组的定义,我们发现,到了最后只有两种情况:
①:abcd abcdabcdabcd 这种情况用上面的公式显而易见是正确的。
②:abcd efhijk abcd 这种情况用上面的公式显而易见是正确的

posted @ 2024-07-26 11:45  Optimist_Skm  阅读(163)  评论(0)    收藏  举报