有趣的字符串题!抢在官方题解之前写一篇题解。
思路
因为需要使字符串代表的整数最小化,所以我们显然要删除前导零后的最终序列长度尽可能小。
我们发现为了达成这个目的,可以把所有的 \(1\) 都聚集到一个区间内,不妨设这个区间是 \([l,r]\)。
那么 \(1 \dots l-1\) 和 \(r+1 \dots n\) 就全是 \(0\),我们可以翻转一次把 \(1 \dots l-1\) 的 \(0\) 去掉,这样 \(r+1 \dots n\) 的 \(0\) 就变成前导零了,可以忽略它们。
容易发现使用更多的翻转次数不优,因为若使用更多翻转操作,一定会使得 \([l,r]\) 的长度增大,这与我们“要使最终序列长度尽可能小”的目的背道而驰。
更进一步,我们可以发现两个比较显然的性质:
-
区间 \([l,r]\) 外的 \(1\) 一定尽可能移到区间内靠近 \(l\) 的位置(因为我们需要翻转序列)。
-
\(\forall l\),有用的区间只有一个。
我们可以双指针把所有有用的区间找出来,答案区间一定在这些区间中长度最小的那些区间之中。
现在问题变成如何比较两个长度相同的区间最终形成的字符串哪个字典序小。
经过简单的观察,又可以发现一个显然的性质:
- 这两个区间填入的 \(1\) 的个数是相同的。
于是问题等价于比较原串的这两个区间翻转之后的字典序(证明不难,可以自己思考一下)。
而这个问题又等价于比较两个前缀翻转之后的字典序。
于是就非常简单了,exkmp,二分哈希,SA 均可。
但是这个做法当区间 \([l,r]\) 的 \(r=n\) 时有点小问题,因为此时有一种特殊的方案是把 \(1 \dots l-1\) 的 \(1\) 放在倒数若干个 \(0\) 上,特判一下即可。
时间复杂度 \(\Theta(n \log n/n)\)。
代码
//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;
}
标签:hsh,Reverse,int,rep,vec,区间,Shrink,define
From: https://www.cnblogs.com/tx-lcy/p/18031152