好题集 (15) - CF 955D Scissors

题目传送门

题意:给定小写字母组成的字符串 \(s,t\),要求从 \(s\) 中取出长为 \(k\) 且不相交的两截,满足 \(t\) 是这两截按顺序拼起来之后的串的子串。输出这两截的起点的任意一种方案或判断无解,\(2k\le n\le 5\times 10^5\)

本系列首道字符串。

首先大力哈希。分两种情况讨论:\(t\) 完全在前半截或后半截,和 \(t\) 被剪成两段。

先考虑前者,此时有 \(m\le k\)。设 \(f_{i}\) 表示前缀 \(i\)\(t\) 的起始位置的出现次数,有转移:\(f_i=f_{i-1}+[s_{i\sim i+m-1}=t]\);算过一遍之后把所有大于 \(i>n-m+1\)\(f_i\) 都设为 \(f_{n-m+1}\),防止出现前缀和非不降的情况。枚举第一段起始位置 \(i\in[1,k]\),对应的终止位置为 \(i+k-1\),由前缀和 \(f_{i+k-1}-f_{i-1}\) 即可判断这一段里有没有 \(t\) 出现;如果出现,则分出来的两段就是 \([i,i+k-1]\)\([i+k,i+2k-1]\),这样 \(t\) 要么是整个出现在前半截,要么是半截在前半截半截在后半截,都合法。同理枚举后半截的起始位置即可。注意这部分判完没发现解不要直接返回。

再考虑后者。贪心地,我们希望前半截尽可能靠左,后半截尽可能靠右以避免重叠。所以设 \(L_i=\min\{x\mid s_{x\sim x+i-1}=t_{1\sim i}\},R_i=\max\{x\mid s_{n-x+1\sim n}=t_{m-x+1\sim m}\}\),只要能求出 \(L,R\),就能找到方案或者判无解;具体地,枚举 \(t\) 在前半截中的长度 \(i\),对应得到在后半截中的长度 \(j=m-i\),如果 \(L_i\)\(R_i\) 都存在(即 \(L_i+i-1\ge k,R_j-j+1\le n-k+1\)),则不难得到两截的起点分别为 \(L_i+i-k\)\(R_j-j+1\);如果前半截终点(即 \(L_i+i-1\))在后半截起点左侧(即 \(L_i+i-1<R_j-j+1\)),就找到了一组解。

考虑如何求 \(L\)。设 \(E_x=\text{LCP}(s_{x\sim n},t)\),则可以发现,\(L_i\) 等价于 \(\min\{x\mid E_x\ge i,x+i-1\ge k\}\)\(E\) 是不难通过哈希二分在 \(O(n\log n)\) 复杂度内求出的,所以考虑如何利用 \(E\)\(L\)

考虑维护出 \(S=\{x\mid E_x\ge i\}\),然后在 \(S\) 中二分找到 \(\min\{x\mid x\in S,x\ge k-i+1\}\)。于是可以在记录原有下标后将 \(E\) 升序排序,再从大到小枚举 \(i\in[1,n]\),用一个 set 维护当前的 \(S\),每轮循环将新增的 \(x\) 塞进去即可。

容易发现将 \(s,t\) 翻转后就可以用类似于求 \(L\) 的手法求出 \(R\)。于是这题做完了,总复杂度 \(O(n\log n)\),轻微卡常,所以不要直接双哈希。

#ifndef ONLINE_JUDGE
#pragma GCC optimize("O3,Ofast,unroll-loops")
#endif
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,avx2,tune=native,avx512f")
#include<iostream>
#include<algorithm>
#include<set>
#define ll long long
using namespace std;

const int N=1e6+5;

int n,m,k;
char s[N],t[N];
int L[N],R[N];set<int>st;
int f[N];

namespace OIfast{
	
	char buf[1<<21],*p1,*p2,*top,buffer[1<<21];
	#define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?0:*p1++)
	#define gc getchar()
	
	inline int read(){
		static int n;n=0;static char c(gc);
		while(!isdigit(c))c=gc;
		while(isdigit(c))n=(n<<3)+(n<<1)+(c^48),c=gc;
		return n;
	}
	
}using namespace OIfast;

namespace KeQing_Forever{
	
	inline ll mod(ll x/*,ll p*/){return /*(x+p)%p*/x;}
	inline ll mul(ll a,ll b/*,ll p*/){return a*b/*%p*/;}
	inline ll add(ll a,ll b/*,ll p*/){return (a+b)/*%p*/;}
	
}using namespace KeQing_Forever;

namespace By_The_Name_of_HASH{
	
	#define pii /*pair<ll,ll>*/ll
	#define x first
	#define y second

	const pii p=/*{1e9+7,1e9+9}*/1e9+7;
	const pii b=/*{1013,211}*/191;
	pii pw[N];
//
//	pii operator + (pii a,ll b){return {add(a.x,b/*,p.x*/),add(a.y,b/*,p.y*/)};}
//	pii operator - (pii a,pii b){return {mod(a.x-b.x/*,p.x*/),mod(a.y-b.y/*,p.y*/)};}
//	pii operator * (pii a,pii b){return {mul(a.x,b.x/*,p.x*/),mul(a.y,b.y/*,p.y*/)};}

	inline void init(){
		pw[0]=/*{1,1}*/1;for(int i=1;i<N;++i)pw[i]=(pw[i-1]*b)%p;
		return ;
	}
	
	struct HASHH{
		
		pii val[N];
		
		inline void build(int n,char *s){
			for(int i=1;i<=n;++i)val[i]=((val[i-1]*b)%p+s[i])%p;
			return ;
		}
		
		inline pii qry(int l,int r){
			return (val[r]-(val[l-1]*pw[r-l+1])%p+p)%p;
		}
		
	}S,T;
	
}using namespace By_The_Name_of_HASH;

struct node{
	int val,id;
}e[N];

inline bool chk(int i,int mid){
	return S.qry(i,i+mid-1)==T.qry(1,mid);
}

inline void calc(int *f,bool flg){
	for(int i=1;i<=n;++i){
		e[i].id=i;
		int res=0,l=0,r=m;
		while(l<=r){
			int mid=(l+r)>>1;
			if(chk(i,mid))l=mid+1,res=mid;
			else r=mid-1;
		}
		e[i].val=res;
	}
	sort(e+1,e+n+1,[](node a,node b){return a.val<b.val;});
	int now=n;
    for(int i=n;i;--i){
		while(now&&e[now].val>=i)st.insert(e[now].id),--now;
		auto it=st.lower_bound(k-i+1);
		if(it==st.end())f[i]=(flg?-1e9:1e9);
		else f[i]=(flg?n-*it+1:*it);
	}
	return st.clear(),void();
}

signed main(){
	init();L[0]=1,R[0]=n;
	n=read(),m=read(),k=read();
	for(int i=1;i<=n;++i){
		char ch=gc;while(!isalpha(ch))ch=gc;
		s[i]=ch;
	}
	for(int i=1;i<=m;++i){
		char ch=gc;while(!isalpha(ch))ch=gc;
		t[i]=ch;
	}
	S.build(n,s),T.build(m,t);
	calc(L,0);
	if(m<=k){
		for(int i=1;i+m-1<=n;++i){
			if(S.qry(i,i+m-1)==T.qry(1,m))f[i]=f[i-1]+1;
			else f[i]=f[i-1];
		}
		for(int i=n-m+2;i<=n*2;++i)f[i]=f[i-1];
		for(int i=1;i<=n-k*2+1;++i)if(f[i+k-1]-f[i-1])return cout<<"Yes\n"<<i<<" "<<i+k,0;
		for(int i=k+1;i<=n-k+1;++i)if(f[i+k-1]-f[i-1])return cout<<"Yes\n"<<i-k<<" "<<i,0;
	}
	reverse(s+1,s+n+1),reverse(t+1,t+m+1);
	S.build(n,s),T.build(m,t);
	calc(R,1);
	for(int i=0;i<=k&&i<=m;++i){
		int j=m-i;
		if(j<1||j>k)continue ;
		if(L[i]>=1e9||R[j]<=-1e9)continue ;
		if(L[i]+i>k&&R[j]-j+1<=n-k+1&&L[i]+i-1<R[j]-j+1)return cout<<"Yes\n"<<L[i]+i-k<<" "<<R[j]-j+1<<"\n",0;
	}
	return cout<<"No\n",0;
}

提交记录

posted @ 2026-07-06 20:58  DX3906_ourstar  阅读(8)  评论(0)    收藏  举报