考虑dp,设f[i]表示至少要用f[i]个s前缀拼出t的前i个字符,容易发现dp值是单调不减的,所以有一种最优的策略:考虑拼出t的前i个字符时,最优的策略就是让本次选的前缀最长。
所以,我们可以考虑kmp,把s+#+t拼成一个新的字符串,求它的next数组,这样就可以O(1)地知道t的每个位置能选的最长前缀长度。然后就能O(n)地dp了。
为什么要加这个#呢?是为了让t这一部分的ne值无法超过s的长度,这样就只能让t和s匹配,而不会出现t和t自身匹配的错误情况。
完整代码:
#include <bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=(a), (i##i)=(b); i<=(i##i); ++i)
#define ROF(i,a,b) for(int i=(a), (i##i)=(b); i>=(i##i); --i)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
#define int long long
const int N = 5e5+5, inf=1e18;
char s[N],t[N],ss[N];
int n,m,f[N]; //f[i]表示至少要用f[i]个s前缀拼出t的前i个字符
int ne[N<<1]; //ss的next数组
signed main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin>>(s+1)>>(t+1);
n=strlen(s+1), m=strlen(t+1);
FOR(i,1,n) ss[i]=s[i]; ss[n+1]='#'; //分隔标记,防止s和t匹配混乱
FOR(j,1,m) ss[j+n+1]=t[j]; ss[n+m+2]='\0';
//构建ss串的next数组
for(int i=2, j=0; i<=n+m+1; i++){
while(j && ss[i]!=ss[j+1]) j=ne[j];
if(ss[i]==ss[j+1]) j++;
ne[i] = j;
}
FOR(i,1,m) f[i]=inf;
FOR(i,1,m){
cmin(f[i], f[i-ne[i+n+1]]+1);
}
if(f[m]==inf) cout<<-1;
else cout<<f[m];
}