好题集 (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;
}
提交记录。

浙公网安备 33010602011771号