【题解】洛谷 P6652 「SWTR-05」String | 20211011 模拟赛 切割字符串(cutstring)【DP 贪心】

题目链接

题目链接

题意

今有一字符串 (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;
}


知识共享许可协议
若文章内无特别说明,公开文章采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
原文地址:https://www.cnblogs.com/wallbreaker5th/p/15394587.html