CodeForces 955D Scissors
昨晚CF比赛比较颓,今天有心情写题解就不错了QWQ
洛谷题目页面传送门 & CodeForces题目页面传送门
给定字符串a,b,|a|=n,|b|=m,求是否可以在a中选2个长度为s的不相交子串,使得b是这2个串按在a中的顺序连起来后得到的串的子串,若可以,输出任一选法。
2≤m≤2s≤n≤5×105。
设从a中选出的2个子串为a1,a2。分2种情况:
- a1或a2完全包含b;
- a1的一个后缀与a2的一个前缀组成b。
第1种情况比较容易,直接将b作为模式串匹配a(这里我用的是Z算法(如果聪明的读者还不知道Z算法是什么,please点击这个)),匹配成功的位置再分2种情况:a1包含b和a2包含b。a1包含b的情况考虑贪心地将a1最左化,好给a2留位置,最后如果放得下直接输出答案return 0;
;a2包含b类似。
第2种情况,设lfti表示满足aj∼j+s−1的长度为i的后缀匹配b的长度为i的前缀的最小的j,riti表示满足aj∼j+s−1的长度为i的前缀匹配b的长度为i的后缀的最大的j,若没有满足条件的j则分别为+∞,−∞。“最小”和“最大”是基于贪心的思想,与第1种情况类似,为的是尽可能给另一个子串留位置。这样最后我们可以枚举i∈[0,s],若m−i∈[0,s]且lfti+s−1<ritm−i,则存在答案(lfti,ritm−i)。
下面考虑lft和rit数组怎么求。以lft为栗例,我们令c=b+!+a,对c跑一遍Z算法。∀i∈[1,n],考虑若a1的后缀从第i位开始,能影响到哪些lftj。显然jmax,因为最多能往后拓展z_{c,m+1+j}个字符,满足这个后缀与b的前缀匹配。j_{\min}呢?j越小,即a1在第i位后面的字符越少,那么a1在第i位前面的字符就越多,多到一定程度就会抵到位置1,所以j_{\min}是刚好抵到的情况,如果不会抵到就是1。于是j_{\min}=\max(s-(i-1),1)。算出影响范围后,我们要去“影响”啊,即令\require{cancel}\forall j\in[j_{\min},j_{\max}],lft_j=\min(lft_j,i+j\cancel{-1}-s\cancel{+1})。这个可以用线段树维护,差分也可以,虽然都是\mathrm O(n\log n),但差分好写一点。
下面讲具体怎么差分:\forall k\in[0,m],维护一个添加序列add_k和删除序列del_k。对于每次影响,在add_{j_{\min}}和del_{j_{\max}+1}里插入i-s。最后维护一个multiset
st(初始为\{+\infty\}),从i=1到i=m递推,每次将add_i里的元素insert
进去,将del_i里的元素erase
掉(注意如果写st.erase(x)
会把所有的x都删掉,应该写st.erase(st.find(x))
),*st.begin()+i
就是lft_i。
rit数组的求法类似,不同在于c=b^\mathrm r+\texttt!+a^\mathrm r,访问z数组时要访问在倒串中的位置,j_{\min}=\max(s-(n-i),1),j_{\max}=z_{c,m+1+(n+1-i)},影响为\forall j\in[j_{\min},j_{\max}],rit_j=\min(rit_j,i-j+1),st初始为\{-\infty\},每次插入i+1,rit_i为*--st.end()-i
。
下面贴代码吧:(写不动了)
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int inf=0x3f3f3f3f;
const int N=500000,M=500000;
int n/*|a|*/,m/*|b|*/,s/*要选的字串的长度*/,t/*|c|*/;
int rev_pos(int pos){return n+1-pos;}//在倒串中的位置
char a[N+5],b[M+5],ra[N+5]/*rev(a)*/,rb[M+5]/*rev(b)*/,c[N+1+M+5]/*b+'!'+a或rb+'!'+ra*/;
void con(char str1[],char str2[]){//令c=str1+'!'+str2
t=0;
for(int i=1;i<=m;i++)c[++t]=str1[i];
c[++t]='!';
for(int i=1;i<=n;i++)c[++t]=str2[i];
}
int z1[N+1+M+1]/*a,b正着的z数组*/,z2[N+1+M+1]/*a,b倒着的z数组*/;
void z_init(int z[]){//Z算法
int zl=0,zr=0;
for(int i=2;i<=t;i++)
if(zr<i){
while(i+z[i]<=t&&c[i+z[i]]==c[1+z[i]])z[i]++;
if(z[i])zl=i,zr=i+z[i]-1;
}
else if(i+z[i-zl+1]<=zr)z[i]=z[i-zl+1];
else{
z[i]=zr-i+1;
while(i+z[i]<=t&&c[i+z[i]]==c[1+z[i]])z[i]++;
zl=i;zr=i+z[i]-1;
}
}
int lft[M+1],rit[M+1];
vector<int> dadd[M+1],ddel[N+1];//差分
multiset<int> st;
int main(){
cin>>n>>m>>s>>a+1>>b+1;
memcpy(ra+1,a+1,n+1);reverse(ra+1,ra+n+1);
memcpy(rb+1,b+1,m+1);reverse(rb+1,rb+m+1);
con(b,a);z_init(z1);
con(rb,ra);z_init(z2);
if(s>=m)//第1种情况
for(int i=1;i<=n;i++)
if(z1[m+1+i]==m){
int l=max(1,i-(s-m)),r=l+s;
if(r+s-1<=n)return cout<<"Yes\n"<<l<<" "<<r,0;
r=min(n,i+s-1)-s+1;l=r-s;
if(l>=1)return cout<<"Yes\n"<<l<<" "<<r,0;
}
//第2种情况
for(int i=1;i<=n;i++){//对lft影响
int l=max(s-(i-1),1),r=z1[m+1+i];
if(l>r)continue;
dadd[l].pb(i-s);if(r<m)ddel[r+1].pb(i-s);
}
st.insert(inf);//初始化
for(int i=1;i<=m;i++){//递推差分求lft
for(int j=0;j<dadd[i].size();j++)st.insert(dadd[i][j]);
for(int j=0;j<ddel[i].size();j++)st.erase(st.find(ddel[i][j]));
lft[i]=*st.begin()+i;
}
for(int i=1;i<=m;i++)dadd[i].clear(),ddel[i].clear();//数据不清空,爆零两行泪
for(int i=1;i<=n;i++){//对rit影响
int l=max(s-(n-i),1),r=z2[m+1+rev_pos(i)];
if(l>r)continue;
dadd[l].pb(i+1);if(r<m)ddel[r+1].pb(i+1);
}
st.clear();st.insert(-inf);//初始化
for(int i=1;i<=m;i++){//递推差分求rit
for(int j=0;j<dadd[i].size();j++)st.insert(dadd[i][j]);
for(int j=0;j<ddel[i].size();j++)st.erase(st.find(ddel[i][j]));
rit[i]=*--st.end()-i;
}
// for(int i=1;i<=m;i++)printf("lft[%d]=%d rit[%d]=%d\n",i,lft[i],i,rit[i]);
for(int i=0;i<=s;i++)if(0<=m-i&&m-i<=s)
if(lft[i]+s-1<rit[m-i])//不相交
return cout<<"Yes\n"<<lft[i]<<" "<<rit[m-i],0;
puts("No");
return 0;
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法