[省选联考 2026] 摩卡串 / string
小摩卡是天才。
给定长度为 \(n\) 的 01 串 \(s\) 和整数 \(k\)。长度为 \(m\) 的 01 串 \(t\) 合法,当且仅当:
-
\(s\) 是 \(t\) 的子串
-
\(t\) 中恰好有 \(k\) 个子串字典序小于 \(s\)
给出任意一个最短的合法 01 串 \(t\),或报告无解。
\(t \leq 5\) 组数据,\(1 \leq n \leq 200\),\(1 \leq k \leq 3000\)。
字典序小于 \(s\) 的字符串有以下两种构成方式:
-
\(s\) 的非自身的前缀,如 \(11001\) 存在符合条件的字符串 \(1,11,110,1100\)
-
第一个不同字符小于 \(s\) 的,如 \(11001\) 存在字符串 \(0\dots,10\dots,11000\dots\)
需要注意的是,上面第二种情况的省略号部分可以为空。
我们考虑 \(t\) 一开始为空,往 \(t\) 的末尾不断追加字符。
对于第一种字符串,考虑构建 kmp 自动机,维护当前在自动机上的节点。
我们容易处理出每个节点能够匹配的前缀个数,累加即可。
同时这样我们可以处理第一条限制,只要过程中跳到了 kmp 自动机的 \(n\) 号节点即可。
对于第二种字符串,容易发现每次加入一个字符会产生两种贡献:
-
新增加的第二种字符串,需要加入 \(0\) 并且能够匹配
-
对于每个第二种字符串,贡献都要加 \(1\)
容易发现,新增的第二种字符串个数和上面的个数一样,都是可以预处理的。
我们需要记录的信息有:
-
目前是否跳到过 kmp 自动机的 \(n\) 号节点
-
当前在 kmp 自动机上的节点编号
-
当前产生的第二种字符串个数
-
当前字典序小于 \(s\) 的子串个数
我们需要找的是从 \((0,0,0,0)\) 到 \((1,?,?,k)\) 的最短路,bfs 解决,构造方案简单。
这样就可以做到 \(O(\sum n k^2)\) 了。
精细分析复杂度可以到 \(O(\sum n k^{1.5})\)。
//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
const bool can_0=true,can_1=true;
const int N=200,M=80,K=3000;
char ch[N+10];
int border[N+10],trans[N+10][2],cnt_1[N+10],cnt_2[N+10];
struct Node{
char num;
short num_1,num_2,num_3,num_4;
}O;
Node lst[2][N+10][M+10][K+10];
bool vis[2][N+10][M+10][K+10];
void solve(){
int n,k;
scanf("%d %d",&n,&k);
for(int i=0;i<=n;i++){
border[i]=0;
trans[i][0]=trans[i][1]=0;
cnt_1[i]=cnt_2[i]=0;
}
for(int i=1;i<=n;i++){
cin>>ch[i];
}
for(int i=2;i<=n;i++){
border[i]=border[i-1];
while(border[i] && ch[border[i]+1]!=ch[i]){
border[i]=border[border[i]];
}
if(ch[border[i]+1]==ch[i]){
border[i]++;
}
}
trans[0][ch[1]-'0']=1;
for(int i=1;i<n;i++){
int cmd=ch[i+1]-'0';
trans[i][cmd]=i+1;
trans[i][cmd^1]=trans[border[i]][cmd^1];
}
trans[n][0]=trans[border[n]][0];
trans[n][1]=trans[border[n]][1];
for(int i=0;i<n;i++){
if(ch[i+1]=='1'){
cnt_2[i]=1;
}
}
for(int i=1;i<n;i++){
cnt_1[i]=1;
}
for(int i=1;i<=n;i++){
cnt_1[i]+=cnt_1[border[i]];
cnt_2[i]+=cnt_2[border[i]];
}
for(int s1=0;s1<2;s1++){
for(int s2=0;s2<=n;s2++){
for(int s3=0;s3<=M;s3++){
for(int s4=0;s4<=k;s4++){
lst[s1][s2][s3][s4]=O;
vis[s1][s2][s3][s4]=false;
}
}
}
}
queue<short> q1,q2,q3,q4;
q1.push(0);
q2.push(0);
q3.push(0);
q4.push(0);
vis[0][0][0][0]=true;
short t1=-1,t2=-1,t3=-1,t4=-1;
while(!q1.empty()){
short u1=q1.front();
q1.pop();
short u2=q2.front();
q2.pop();
short u3=q3.front();
q3.pop();
short u4=q4.front();
q4.pop();
if(can_0){
int v1=u1,v2=u2,v3=u3,v4=u4;
v3+=cnt_2[v2];
v2=trans[v2][0];
if(v2==n) v1=true;
v4+=cnt_1[v2];
v4+=v3;
if(v3<=M && v4<=k){
if(!vis[v1][v2][v3][v4]){
vis[v1][v2][v3][v4]=true;
lst[v1][v2][v3][v4]=(Node){'0',u1,u2,u3,u4};
q1.push(v1);
q2.push(v2);
q3.push(v3);
q4.push(v4);
if(v1==1 && v4==k){
t1=v1;
t2=v2;
t3=v3;
t4=v4;
break;
}
}
}
}
if(can_1){
int v1=u1,v2=u2,v3=u3,v4=u4;
v2=trans[v2][1];
if(v2==n) v1=true;
v4+=cnt_1[v2];
v4+=v3;
if(v3<=M && v4<=k){
if(!vis[v1][v2][v3][v4]){
vis[v1][v2][v3][v4]=true;
lst[v1][v2][v3][v4]=(Node){'1',u1,u2,u3,u4};
q1.push(v1);
q2.push(v2);
q3.push(v3);
q4.push(v4);
if(v1==1 && v4==k){
t1=v1;
t2=v2;
t3=v3;
t4=v4;
break;
}
}
}
}
}
string ans;
if(t1==-1 && t2==-1 && t3==-1 && t4==-1){
ans="Impossible";
}
else{
while(lst[t1][t2][t3][t4].num){
ans+=lst[t1][t2][t3][t4].num;
Node tmp=lst[t1][t2][t3][t4];
t1=tmp.num_1;
t2=tmp.num_2;
t3=tmp.num_3;
t4=tmp.num_4;
}
reverse(ans.begin(),ans.end());
}
cout<<ans<<endl;
}
int main(){
int test_id,test_cnt;
scanf("%d %d",&test_id,&test_cnt);
while(test_cnt--){
solve();
}
return 0;
}
这是简单题啊,那我为啥没过。

浙公网安备 33010602011771号