[ABC257G] Prefix Concatenation 题解

[ABC257G] Prefix Concatenation Solution

更好的阅读体验戳此进入

题面

给定仅存在小写英文字母的字符串 $ S, T $。你需要将 $ T $ 分割成 $ k $ 个 $ S $ 的前缀(或着说用 $ S $ 的若干个前缀组成 $ T $),最小化 $ k $,输出最小值。若 $ k $ 不存在输出 -1

Solution

首先 $ O(n^2) $ 的 DP 显然,我们记 $ S(i, j), T(i, j) $ 为对应字符串 $ [i, j] $ 的子串,令 $ dp(i) $ 表示考虑 $ T(1, i) $ 的最小 $ k $。易得转移:

\[dp(i) = \min_{j \lt i \land S(1, i - j) = T(j + 1, i)}dp(j) + 1 \]

不难发现这个 $ \texttt{1D1D} $ 的 DP 是 $ O(n^2) $ 的无法通过,考虑优化。

首先有一个思路,已知 $ dp(i) $ 单调不降,证明显然,考虑若 $ dp(i) \gt dp(i + 1) $,那么在 $ dp(i + 1) $ 的方案中将最后一部分去掉第 $ i + 1 $ 个一定可以变为 $ dp(i) $ 且 $ k $ 不劣,得证。

然后根据这个思路我们每次转移只需要找到最小的合法 $ j $ 转移即可,可以通过 KMP 中的 next 数组维护。

具体地,不难发现我们这个东西求的可以转化为求 border,具体地,我们将 $ S $ 后追加一个占位符,然后再连接上 $ T $,记 P = S + '#' + T,这样我们对 $ P $ 跑一遍 KMP 的建立 next 数组过程,不难发现对于转移 $ i $ 时需要的 $ j $,即为 $ i - nxt(len_S + 1 + i) $,这里的 $ +1 $ 即为我们添加的占位符 #

最终 DP 优化为 $ \texttt{1D0D} $,复杂度 $ O(n) $,可以通过。

同时还有一种方法,发现修改状态为后缀可以支持逆序转移,于是转移变为:

\[dp(i) = \min_{j \gt i \land S(1, j - i) = T(i, j - 1)}dp(j) + 1 \]

此时发现每次可以转移的 $ j $ 是连续的,对应 $ S $ 和 $ T(i, len_T) $ 的 LCP,于是可以通过哈希 + 二分处理每次转移的 LCP 长度,然后线段树优化 DP 即可,最终复杂度 $ O(n \log n) $,劣于正向转移但也可以通过,且思路更直观,仅代码实现略长。

Code

#define _USE_MATH_DEFINES
#include <bits/stdc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template < typename T = int >
inline T read(void);

string S, T;
char s[1100000];
int dp[1100000];
int nxt[1100000];

int main(){
    memset(dp, 0x3f, sizeof dp);
    cin >> S >> T;
    sprintf(s + 1, "%s#%s", S.c_str(), T.c_str());
    int lenS = S.length(), lenT = T.length();
    dp[lenS + 1] = 0;
    int cur(0);
    for(int i = 2; i <= lenS + lenT + 1; ++i){
        while(cur && s[cur + 1] != s[i])cur = nxt[cur];
        if(s[cur + 1] == s[i])++cur;
        if(i > lenS + 1)dp[i] = dp[i - cur] + 1;
        nxt[i] = cur;
    }printf("%d\n", dp[lenS + lenT + 1] < 0x3f3f3f3f ? dp[lenS + lenT + 1] : -1);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    int flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2022_12_09 初稿

posted @ 2023-01-07 15:42  Tsawke  阅读(51)  评论(0)    收藏  举报