[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 $。易得转移:
不难发现这个 $ \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) $,可以通过。
同时还有一种方法,发现修改状态为后缀可以支持逆序转移,于是转移变为:
此时发现每次可以转移的 $ 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 初稿

浙公网安备 33010602011771号