1 条题解
-
0
自动搬运
来自洛谷,原作者为

A6n6d6y6
QAQ 小A就是我搬运于
2025-08-24 23:06:15,当前版本为作者最后更新于2025-06-13 17:50:49,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
问题描述
传送门。见原题题目描述。
思路
你说得对,但是只用哈希、二分、线段树可以轻松通过。
分为两种情况:
-
在原串中不重叠地出现了两次;
-
形如 ,如果原串中出现了 ,那么就可以将两边的 拼接起来。
对于第一种情况,二分一个长度 ,对于所有哈希值相同的子串,记录最后一次出现的左端点,判断其他右端点是否在它的左边,就可以处理不重叠的要求了,用了一个
std::map,时间复杂度 。对于第二种情况,类似于 [NOI2016] 优秀的拆分,先考虑形如 的串。不妨设 为以 为右端点最长 串的一半长度,不妨设 为以 为左端点最长 串的一半长度,答案即为:
好,现在考虑如何描述所有 串,对于长度为 的 串:
如果我们在字符串上每隔 设置一个观察点,那么所有 串必然包含两个观察点。
相邻观察点的 LCP(最长公共前缀) 与 LCS(最长公共后缀) 拼起来的字符串即为经过的两点上的最长 串。若 LCP 与 LCS 的和大于 ,说明它们相交,可以有让一段区间多出长度为 的选择。例如 ,其中括号是重叠的部分,那么出现了三个 串:。
可以分析,这样的一段区间不会超过 个,如果用哈希求 LCP 和 LCS,时间复杂度是 的。
现在只需要对于每个左端点和右端点,当作一个操作,取一个最大值,有 次修改,最后统一查询,可以离线下来对于长度 排序,从小到大处理,就成了区间赋值,用线段树处理,时间复杂度也是 的。
最后将两种答案取最大值,得到最终答案,总时间复杂度 。
综上,我们在 的时间复杂度下解决这道缝合问题。
细节
自然溢出?被卡了。单哈希?也被卡了。所以只能写双模哈希。
注意多个二分的最大边界条件。
代码
AC记录。代码轻微压行、卡常。
#include<bits/stdc++.h> #define int long long using namespace std; const int maxn=1e5+10,mod1=1e9+7,mod2=1e9+9,base1=19491001,base2=19370707; struct SegmentTree{ struct node{int l,r,w;}t[maxn<<2]; int ls(int u){return u<<1;} int rs(int u){return u<<1|1;} void pushdown(int u){ if(!t[u].w)return; t[ls(u)].w=t[u].w; t[rs(u)].w=t[u].w; t[u].w=0; } void build(int u,int l,int r){ t[u]={l,r,0}; if(l==r)return; int mid=(l+r)>>1; build(ls(u),l,mid); build(rs(u),mid+1,r); } void modify(int u,int l,int r,int w){ if(r<t[u].l||t[u].r<l)return; if(l<=t[u].l&&t[u].r<=r){t[u].w=w;return;} pushdown(u); modify(ls(u),l,r,w); modify(rs(u),l,r,w); } int query(int u,int x){ if(x<t[u].l||t[u].r<x)return 0; if(x<=t[u].l&&t[u].r<=x)return t[u].w; pushdown(u); return query(ls(u),x)^query(rs(u),x); } }Tl,Tr;//区间赋值线段树 struct num{ int h1,h2;num(int a=0,int b=0){h1=a;h2=b;} friend bool operator<(const num &a,const num &b){ if(a.h1!=b.h1)return a.h1<b.h1; return a.h2<b.h2; } friend bool operator ==(const num &a,const num &b){return a.h1==b.h1&&a.h2==b.h2;} friend num operator + (const num &a,const num& b){return num((a.h1+b.h1)%mod1,(a.h2+b.h2)%mod2);} friend num operator - (const num &a,const num& b){return num((a.h1-b.h1+mod1)%mod1,(a.h2-b.h2+mod2)%mod2);} friend num operator * (const num &a,const num& b){return num((a.h1*b.h1)%mod1,(a.h2*b.h2)%mod2);} }h[maxn],p[maxn],base(base1,base2);//双哈希结构体 int n; num get(int l,int r){return h[r]-h[l-1]*p[r-l+1];} int lcp(int x,int y){ int l=0,r=x,ans=0; while(l<=r){ int mid=(l+r)>>1; if(get(x-mid+1,x)==get(y-mid+1,y))l=mid+1,ans=mid; else r=mid-1; } return ans; }//哈希求LCP int lcs(int x,int y){ int l=0,r=n-y+1,ans=0; while(l<=r){ int mid=(l+r)>>1; if(get(x,x+mid-1)==get(y,y+mid-1))l=mid+1,ans=mid; else r=mid-1; } return ans; }//哈希求LCS struct node{int l,r,w;};vector<node>Vr,Vl; bool cmp(const node& a,const node& b){ if(a.w!=b.w)return a.w<b.w; if(a.l!=b.l)return a.l<b.l; return a.r<b.r; }//排序线段 map<num,int>st; bool check(int mid){ st.clear(); for(int i=n-mid+1;i>=1;i--){ const num&& h=get(i,i+mid-1); if(!st.count(h))st[h]=i; else if(st[h]>=i+mid)return 1; } return 0; }//第一种情况 signed main(){ // freopen("str.in","r",stdin); // freopen("str.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); string s;cin>>s;s=' '+s; n=s.size()-1;p[0]={1,1}; for(int i=1;i<=n;i++){ h[i]=h[i-1]*base+num{s[i],s[i]}; p[i]=p[i-1]*base; }//哈希初始化 Vl.clear();Vr.clear(); for(int i=1;i<=n;i++){ for(int j=i,k=i+i;k<=n;j+=i,k+=i){ int l=max(k-lcp(j,k)+i,k),r=min(k+lcs(j,k)-1,k+i-1); if(l>r)continue; Vl.push_back({l,r,i}); Vr.push_back({l-i*2+1,r-i*2+1,i}); } }//求线段 sort(Vl.begin(),Vl.end(),cmp); sort(Vr.begin(),Vr.end(),cmp); Tl.build(1,1,n);Tr.build(1,1,n); for(auto [l,r,w]:Vl)Tl.modify(1,l,r,w); for(auto [l,r,w]:Vr)Tr.modify(1,l,r,w);//离线赋值 int l=0,r=n/2,ans=0; while(l<=r){ int mid=(l+r)>>1; if(check(mid))l=mid+1,ans=mid; else r=mid-1; }//第一种情况 for(int i=1;i<n;i++) ans=max(ans,Tl.query(1,i)+Tr.query(1,i+1));//第二种情况 cout<<ans; return 0; } -
- 1
信息
- ID
- 10984
- 时间
- 1500ms
- 内存
- 500MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者