CF762C Two strings 题解

洛谷传送门

CF 传送门

题意

给你两个字符串 \(a\)\(b\),你可以在 \(b\) 中删去尽量短的子段,使得 \(b\)\(a\) 的子序列。求出最后的 \(b\)

思路

真是奇了怪了,这种题洛谷题解里竟然没有双指针的做法?

首先考虑判断一个字符串 \(b\) 是否是另一个字符串 \(a\) 的子序列。这个问题相信学过点 OI 的人都会做。

其实在做上面这个题的时候,因为贪心,\(b\) 字符串的最右边字符对应在 \(a\) 中一定是最靠左的。那么很容易,也可以求出 \(b\) 的最左边字符在 \(a\) 中最靠右的位置。

现在再看原题,其实就是把 \(b\) 字符串分成前后两个部分,其中前一个部分在 \(a\) 中尽量靠左,后一个部分在 \(a\) 中尽量靠右,并且两个不相交。

然后就好做了,先对 \(b\) 的每一个前缀求出「最右边字符对应在 \(a\) 中最靠左的位置」,每一个后缀求出「最左边字符对应在 \(a\) 中最靠右的位置」,分别用 \(L\)\(R\) 数组保存。

最后我们要选出两个断点 \(l\)\(r\),分别代表分成的前后两个部分的右端点和左端点,并且 \(L_l<R_r\),同时 \(r-l\) 要尽量的小。

所以这个怎么求呢?等等, \(L\)\(R\) 单调递增?

那不是妥妥的双指针吗?

然后这题就做完了,时间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
int n,m;
string a,b;
int L[100005],R[100005];
int ans=1e9,k1,k2;//最短r-l,以及两个断点的记录 
int main(){
	cin>>a>>b;
	n=a.length(),m=b.length();
	a=" "+a,b=" "+b;
	int r=1;//指针 
	for(int i=1;i<=m;i++){//预处理L数组 
		while(r<=n&&a[p]!=b[i]) r++;
		L[i]=r,r=min(r+1,n+1);
	}
	r=n;
	for(int i=m;i>=1;i--){//预处理R数组 
		while(r>=1&&a[p]!=b[i]) r--;
		R[i]=r,r=max(r-1,0);
	}
	if(L[1]==n+1&&R[m]==0){//这里特判一下,或者在最后特判也行 
		printf("-\n");
		return 0;
	}
	L[0]=0,R[m+1]=n+1;//防越界特判的操作 
	r=1;
	for(int l=0;l<=m;i++){//计算答案,双指针板板 
		if(L[l]==n+1) break;
		r=max(r,l+1);
		while(L[l]>=R[r]){
			r++; 
		}
		if(r-l-1<ans){
			ans=r-l-1,k1=l,k2=r;
		}
	}
	for(int i=1;i<=m;i++){
		if(i<=k1||i>=k2){
			cout<<b[i];
		}
	}
	printf("\n");
	return 0;
}
posted @   candy0014  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 如何调用 DeepSeek 的自然语言处理 API 接口并集成到在线客服系统
点击右上角即可分享
微信分享提示