【洛谷5890】小欧与回文串构造
构造。
题目大意:
给定 $n,k$,要求构造长度恰好为 $n$ 的 01
串,使得其中本质不同的回文子串个数恰好为 $k$。
题解:
当 $n$ 较小的时候,可以暴力枚举。
当 $n$ 较大时,有以下构造方法:
我们构造的串为 001011001011001011...
,即 001011
不断循环。
发现其前缀 001011001
恰好包含 $8$ 个不同回文子串,且之后继续循环仍然为 $8$ 个。
那我们可以先将 $8$ 个分配给这里,然后 $k-8$ 个的构造,可以在串前面加上 $k-8$ 个 0
。
这样就可以构造任意长度的 $8\leq k <n$ 的串了。
当 $n$ 较大时,$k<8$ 没有方法,而 $k=n$ 时这种方法会少一个(前缀长度不到 $9$),这时只要 $k$ 个 0
就可以了。
Code:
#include<iostream>
#include<cstring>
#include<string>
#include<set>
using namespace std;
int T,n,k;
std::set<string>st;
bool check(const string&s){
for(int i=0;i<s.length();++i)
if(s[i]!=s[s.length()-i-1])return 0;
return 1;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
for(cin>>T;T--;){
cin>>n>>k;
if(n<=16){
const int C=1<<n;
bool o=0;
for(int S=0;S<C;++S){
int x=0;
string s="";
st.clear();
for(int i=0;i<n;++i)
s+=(char)((S>>i&1)^'0');
for(int l=0;l<n;++l){
string nw="";
for(int r=l;r<n;++r){
nw+=s[r];
if(check(nw)&&!st.count(nw))
st.insert(nw),++x;
}
}
if(x==k){
cout<<"Yes\n"<<s<<'\n';
o=1;
break;
}
}
if(!o)cout<<"No\n";
}else
if(n==k){
cout<<"Yes\n";
for(int i=0;i<k;++i)cout.put('1');
cout<<'\n';
}else{
if(k<8)cout<<"No\n";else{
cout<<"Yes\n";
k-=8;
string ans="";
while(ans.length()+6<=n-k)ans+="001011";
if(ans.length()<n-k)ans+="0";
if(ans.length()<n-k)ans+="0";
if(ans.length()<n-k)ans+="1";
if(ans.length()<n-k)ans+="0";
if(ans.length()<n-k)ans+="1";
for(int i=0;i<k;++i)cout.put('0');
cout<<ans<<'\n';
}
}
}
return 0;
}