题意
今有一字符串 (s),一次操作可以删去其一个前/后缀,要求删去部分是剩余部分的子串。用最小的操作次数使得 (s) 变为 (t)。(nleq 5 imes 10^3)。
题解
反过来考虑扩展 (t),则每次一定是贪心扩展,(O(n^2)) DP;能扩展的范围与 LCP 有关,亦可 (O(n^2)) DP。
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10;
char s[N],t[N];
int lcp[N][N],lcs[N][N],f[N][N];
int main(){
freopen("cutstring.in","r",stdin);
freopen("cutstring.out","w",stdout);
cin>>(s+1)>>(t+1);
int n=strlen(s+1),m=strlen(t+1);
for(int i=n;i>=1;--i){
for(int j=n;j>i;--j)
lcp[i][j]=s[i]==s[j]?lcp[i+1][j+1]+1:0;
}
for(int i=n;i>=1;i--)
for(int j=i+1;j<=n;j++){
lcp[i][j]=min(lcp[i][j],j-i);
lcp[i][j]=max(lcp[i][j],lcp[i+1][j]);
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++)
lcs[i][j]=s[i]==s[j]?lcs[i-1][j-1]+1:0;
}
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
lcs[i][j]=min(lcs[i][j],j-i);
lcs[i][j]=max(lcs[i][j],lcs[i][j-1]);
}
}
memset(f,0x3f,sizeof(f));
for(int i=1;i<=n;i++){
bool b=1;
for(int j=1;j<=m&&b;j++)
if(s[i+j-1]!=t[j])b=0;
if(b)f[i][i+m-1]=0;
}
for(int len=m;len<=n;len++){
for(int i=1,j=len;j<=n;i++,j++){
if(f[i][j]>1e9)continue;
f[i][j+lcp[i][j+1]]=min(f[i][j+lcp[i][j+1]],f[i][j]+1);
f[i-lcs[i-1][j]][j]=min(f[i-lcs[i-1][j]][j],f[i][j]+1);
}
}
cout<<(f[1][n]<1e9?f[1][n]:-1)<<endl;
}