abc257(G)
题意很明显,给定字符串S和字符串T(字符串首地址为1)。取出n个字符串S的前缀按一定次序拼接起来的字符串与字符串T相同。输出n的最小值,无解输出-1.
很巧妙的一道dp + kmp 题。
首先我们将 dp[i] 表示成拼接 T 的前缀最少需要多少个 S 的前缀。
可以证明 dp[i] <= dp[i + 1] 对应所有 0 < i < |T|。
假如 dp[i] > dp[i + 1],我们可以删掉第i个字符使得 dp[i] = (dp[i + 1] - 1 或者 dp[i])
因此 dp[i] <= dp[i + 1].因此在第 i 个字符时我们要保证前缀越大越好。
对此我们重新构造出一个字符串 P = S +'#' + T。然后对 P 进行KMP算法可以算出在T的某个后缀为S的某个前缀。这时#号的作用出来了,使得T的后缀必然在#号后面,S的前缀必然在#号的前面。ne[i]的值即为后缀的最大的长度。首先给 # 位置dp的值初始化为0。
- dp[i] = max(dp[i], dp[i - ne[i]] + 1);// 更新dp[i]的值。
最后dp[n]为最终答案。如果dp[i] 为无穷大则无解.
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5, INF = 0x3f3f3f3f;
char s[N];
int ne[N];
int dp[N];
int main() {
memset(dp, 0x3f, sizeof(dp));
scanf("%s", s + 1);
int n = strlen(s + 1);
s[n + 1] = '#', dp[n + 1] = 0;
scanf("%s", s + 2 + n);
n = strlen(s + 1);
for (int i = 2, j = 0; i <= n; i++) {
while (j && s[i] != s[j + 1]) j = ne[j];
if (s[i] == s[j + 1]) j++;
dp[i] = min(dp[i], dp[i - j] + 1);
// printf("%c %d %d %d\n",s[i], i - j, i, dp[i]);
ne[i] = j;
}
if (dp[n] == INF) puts("-1");
else printf("%d\n", dp[n]);
// puts(s + 1);
return 0;
}