abc257(G)

G - Prefix Concatenation

题意很明显,给定字符串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;
}
posted @ 2022-06-27 20:02  什么都不会的娃娃  阅读(151)  评论(0)    收藏  举报