Shrink-Reverse
有趣的字符串题!抢在官方题解之前写一篇题解。
思路
因为需要使字符串代表的整数最小化,所以我们显然要删除前导零后的最终序列长度尽可能小。
我们发现为了达成这个目的,可以把所有的
那么
容易发现使用更多的翻转次数不优,因为若使用更多翻转操作,一定会使得
更进一步,我们可以发现两个比较显然的性质:
-
区间
外的 一定尽可能移到区间内靠近 的位置(因为我们需要翻转序列)。 -
,有用的区间只有一个。
我们可以双指针把所有有用的区间找出来,答案区间一定在这些区间中长度最小的那些区间之中。
现在问题变成如何比较两个长度相同的区间最终形成的字符串哪个字典序小。
经过简单的观察,又可以发现一个显然的性质:
- 这两个区间填入的
的个数是相同的。
于是问题等价于比较原串的这两个区间翻转之后的字典序(证明不难,可以自己思考一下)。
而这个问题又等价于比较两个前缀翻转之后的字典序。
于是就非常简单了,二分哈希,SA 均可。
但是这个做法当区间
时间复杂度
代码
//A tree without skin will surely die.
//A man without face is invincible.
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define sqr(x) ((x)*(x))
#define all(x) (x).begin(),(x).end()
#define Tim ((double)clock()/CLOCKS_PER_SEC)
#define mid ((l+r)>>1)
#define lowbit(x) (x&-x)
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
int const N=5e5+10;
int const mod=1e9+7;
int n,k,sm,pw[N],qz0[N],qz1[N];string s;
struct Hash_Table{
#define ull unsigned long long
int const B=457;
ull hsh[N],bse[N];
inline void init(string s){
s=" "+s,hsh[0]=bse[0]=1;
rep(i,1,n) hsh[i]=hsh[i-1]*B+(s[i]-'0')*31+233;
rep(i,1,n) bse[i]=bse[i-1]*B;
}
inline ull qry(int l,int r){
++l,++r;
return hsh[r]-hsh[l-1]*bse[r-l+1];
}
}T;
inline int qry0(int l,int r){if (l>r) return 0;if (r<0) return 0;if (!l) return qz0[r];return qz0[r]-qz0[l-1];}
inline int qry1(int l,int r){if (l>r) return 0;if (r<0) return 0;if (!l) return qz1[r];return qz1[r]-qz1[l-1];}
inline string get(string ans){
int r=0;
while (ans[r]=='0') ++r;
string t="";
rep(i,r,(int)ans.size()-1) t+=ans[i];
return t;
}
inline string chk(int L,int R){
string t=s,ans=s;
vector<int>vec;
rep(i,L,R) if (t[i]=='0') vec.push_back(i);
int re=0;
rep(i,0,L-1) if (t[i]=='1') swap(t[i],t[vec[re]]),++re;
per(i,n-1,R+1) if (t[i]=='1') swap(t[i],t[vec[re]]),++re;
if (re<k){
t=get(t),reverse(all(t)),t=get(t);
if (t.size()<ans.size()) ans=t;
else if (t.size()==ans.size()) ans=min(ans,t);
}
return ans;
}
inline int qry(int x,int y){
int l=1,r=y+1,ans=0;
while (l<=r)
if (T.qry(x-mid+1,x)==T.qry(y-mid+1,y)) l=(ans=mid)+1;
else r=mid-1;
return ans;
}
inline string work(){
string ans=s;
vector<int>vec;
per(i,n-1,0) if (s[i]=='0') vec.push_back(i);
int m=k,l=0;
rep(i,0,n-1){
if (!m) break;
if (l==(int)vec.size()) break;
if (ans[i]=='1'){
if (vec[l]<i) break;
swap(ans[i],ans[vec[l]]),++l,--m;
}
}
ans=get(ans),l=0;
int ansl=-1e18,ansr=1e18;
rep(i,0,n-1){
while (l<n && (qry1(0,i-1)+qry1(l+1,n-1)>qry0(i,l) || qry1(0,i-1)+qry1(l+1,n-1)>=k)) ++l;
if (l>=n) break;
if (l-i+1>ansr-ansl+1) continue;
if (l-i+1<ansr-ansl+1){ansl=i,ansr=l;continue;}
int g=qry(l,ansr);
if (g==ansr+1) continue;
if (s[l-g]<s[ansr-g]) ansl=i,ansr=l;
}
if (ansr-ansl+1<(int)ans.size()) ans=chk(ansl,ansr);
else if (ansr-ansl+1==(int)ans.size()) ans=min(ans,chk(ansl,ansr));
return ans;
}
void solve(){
cin>>n>>k>>s,T.init(s),qz0[0]=s[0]=='0',qz1[0]=s[0]=='1';
rep(i,1,n-1) qz0[i]=qz0[i-1]+(s[i]=='0'),qz1[i]=qz1[i-1]+(s[i]=='1');
string ans=work();
int res=0;
rep(i,0,(int)ans.size()-1)
res+=(ans[i]=='1')*pw[ans.size()-1-i],res%=mod;
cout<<res<<'\n';
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
pw[0]=1;
rep(i,1,N-1) pw[i]=pw[i-1]*2%mod;
int t=1;
// cin>>t;
while (t--) solve();
cerr<<"Running Time: "<<(double)clock()/CLOCKS_PER_SEC<<" s\n";
return 0;
}
本文作者:Tx_Lcy
本文链接:https://www.cnblogs.com/tx-lcy/p/18031152
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2023-02-24 题目乱做笔记