看不出哪里有问题了,悬赏关注。
首先,用 $dp[i]$ 表示把前 $i$ 位的字符完全匹配需要的最少词缀数(下标均从 $1$ 开始)。那么,我们可以从点 $i+1$ 开始,向后逐位与字符串 $T$ 比较。设此时匹配到了 $T$ 中的第 $j$ 位,如果相等,则易得转移方程:
$$dp[i+j]=\min(dp[i+j],dp[i]+1)$$
如果不相等或到达了字符串 $T$ 末尾,则证明在此之后不会更长的有魔法词缀,可以结束这一次匹配,令 $i=i+1$ 计算下一位即可。
很明显,这个算法的时间复杂度是 $O(|S||T|)$ 的,当数据范围达到 $|S|,|T|\le10^6$ 时,算法必然超时。
考虑优化这个算法,我们知道,如果不相等或到达了字符串 $T$ 末尾,失配后是可以直接跳过一部分不可能产生新的解的数据。这样就自然而然地想到了用这个思想把单模字符串匹配优化到 $O(|S|+|T|)$ 的 KMP 算法。
借助 KMP 的思想,首先求出字符串 $T$ 的 $next$ 数组,然后开始按照 KMP 的方式匹配:(设此时文本串匹配到第 $i$ 项,模式串匹配到第 $j$ 项)
设置一个名为 $now$ 的临时变量,用于存储如果匹配的最少词缀数。
可以直接逐位比较。如果相等,则按照 KMP 思想,将模式串和文本串指针一起后移,令 $dp[i]=now$ 后比较下一位。
如果不相等,可以令 $j=next[j]$ 之后重新计算 $now$ 的值。因为一旦匹配失败,只能再次选择一个词缀。每次 KMP 算法在匹配失败后,会利用最长公共前后缀的性质使得文本串指针 $i$ 不往前跳。而每次利用最长公共前后缀的性质,会改变模式串匹配的起始位置,所以需要重新计算 $now$ 的值。可以直接用 $dp[i-j]$ 计算出模式串匹配的起始位置的前一个位置,把 $now$ 的值更新为 $dp[i-j]+1$ 以保证正确性。模式串匹配到末尾也是同理。
DP边界:$dp[0]=1$
DP目标:$dp[|S|]$
时间复杂度:$O(|S|+|T|)$
注意,由于有无解的情况,所以当 $next$ 数组跳到 $-1$ 时,应该直接判定无解并输出 `Fake`。因为如果 $next$ 数组跳到 $-1$ 证明匹配第一个字符就失配了,此时后面没有办法再进行匹配,无解。