题解:P15650 [省选联考 2026] 摩卡串 / string
Mr_Terminator
·
·
题解
::::info[以下是场上对答案长度上界的极唐诗脑补证明,如果不想看可以跳过。]
-
定理 1:首先我们需要确认的一点是:答案如果存在,则最短长度 \le 2m。
记答案串是 t。我们将所有小于 s 的区间中所有位置作出一个标记。
- 引理 1:如果 r_0\le r,t_{[l,r]}<s,则 t_{[l,r_0]}<s。
证明:显然。
- 引理 2:对于被标记的一个下标 i,存在 l 使得 t_{[l,i]}<s。
证明:根据标记的定义,存在 l,r 使得 l\le i\le r,t_{[l,r]}<s。由于 i\le r,故此时 t_{[l,i]}<s。由此也可以知道被标记的下标数量不超过 m。
- 引理 3:若存在满足条件的 t,则存在未被标记的下标数量 \le m 的 t。
证明:由于 m>0,故若存在 t,则 s\not=\texttt{0}。若 |s|=1,即 s=\texttt{1},则在 m=1 时构造 t=\texttt{10},否则构造 t=\texttt{0}+(m-1)\times\texttt{1},|t|\le 2m。
若 |s|>1,则考虑调整法。假设你有一个未被标记的下标数量 >m 的 t,则总能找到一个前面没有字符或者未被标记的位置,删除之。
::::
总之就是答案若存在,则长度 \le 2m。
记答案为 t。记 C_l=\operatorname{LCP}(t_{[l,|t|]},s),R_l=l+C_l-1。
-
若 C_l=n,则 l 作为左端点贡献了 n-1 个 <s 的区间,并且使得 s 是 t 的子串。
-
若 C_l<n,s_{C_l+1}=0,则 l 作为左端点贡献了 C_l 个区间。
-
若 C_l<n,s_{C_l+1}=1,则 l 作为左端点贡献了 |t|-l+1=C_l+(|t|-R_l) 个区间。
记 $f_{x,i,0/1,j}$ 表示**删去后 $x$ 个字符的串**匹配 KMP 的状态是 $i$,存在/不存在 $0\le y\le x$ 使得加入后 $x$ 个字符中的前 $y$ 个字符后 KMP 状态是 $n$,后 $x$ 个字符作为左端点贡献了总共 $j$ 个区间的可行性。
初始化和转移是简单的,预处理将所有 KMP 状态匹配 $0,1$ 或滚木时沿着 $\operatorname{fail}$ 跳时,被顶掉的状态贡献的区间数即可。时间复杂度 $O(Tnm^2)$,过不去,但是注意到这里是可行性 dp 且有关 $j$ 这一维在转移时涉及到了平移······咳咳······
时间复杂度 $O(Tnm\times\frac{m}{\omega})$,可以通过。**更多实现细节看代码**。
```cpp
#include<bits/stdc++.h>
using namespace std;
int cid,T,n,K,kmp[205],mat[205][2],cnt0[205],sum0[205],sum1[205],sum2[205];
string s;bool a[205];
bitset<3005>B[6005][205][2];
int mtch(int x,bool c){
if(x==-1)return 0;
if(x<n&&a[x+1]==c)return x+1;
return mtch(kmp[x],c);
}
int main(){
cin.tie(0)->sync_with_stdio(0);
cin>>cid>>T;
while(T--){
cin>>n>>K>>s;for(int i=1;i<=n;i++)a[i]=(s[i-1]=='1');
kmp[0]=-1;for(int i=1;i<=n;i++)kmp[i]=mtch(kmp[i-1],a[i]);
for(int i=0;i<=n;i++){
mat[i][0]=mtch(i,0);mat[i][1]=mtch(i,1);
cnt0[i]=sum0[i]=sum1[i]=sum2[i]=0;
for(int x=i;x!=-1;x=kmp[x]){
if(x==n){sum0[i]+=n-1;sum1[i]+=n-1;sum2[i]+=n-1;}
else{
sum2[i]+=x;
if(a[x+1]){cnt0[i]++;sum0[i]+=x;}
else sum1[i]+=x;
}
}
}
for(int i=0;i<=n;i++)if(sum2[i]<=K)B[0][i][i==n][sum2[i]]=1;
bool satis=0;int len=0,ii=0,oo=1,kk=K;
for(int r=1;r<=2*K;r++){
for(int i=0;i<=n;i++)for(int o=0;o<2;o++){
if(sum0[i]+cnt0[i]*r<=K)B[r][i][o|(i==n)]|=(B[r-1][mat[i][0]][o]<<(sum0[i]+cnt0[i]*r));
if(sum1[i]<=K)B[r][i][o|(i==n)]|=(B[r-1][mat[i][1]][o]<<sum1[i]);
}
if(B[r][0][1][K]==1){satis=1;len=r;break;}
}
if(!satis)cout<<"Impossible\n";
else{
for(int r=len;r>0;r--){
if(ii==n&&oo==1&&sum0[ii]+cnt0[ii]*r<=kk&&B[r-1][mat[ii][0]][0][kk-sum0[ii]-cnt0[ii]*r]==1){
cout<<0;kk-=sum0[ii]+cnt0[ii]*r;ii=mat[ii][0];oo=0;
}
else if(sum0[ii]+cnt0[ii]*r<=kk&&B[r-1][mat[ii][0]][oo][kk-sum0[ii]-cnt0[ii]*r]==1){
cout<<0;kk-=sum0[ii]+cnt0[ii]*r;ii=mat[ii][0];
}
else if(ii==n&&oo==1&&sum1[ii]<=kk&&B[r-1][mat[ii][1]][0][kk-sum1[ii]]==1){
cout<<1;kk-=sum1[ii];ii=mat[ii][1];oo=0;
}
else if(sum1[ii]<=kk&&B[r-1][mat[ii][1]][oo][kk-sum1[ii]]==1){
cout<<1;kk-=sum1[ii];ii=mat[ii][1];
}
}
cout<<'\n';
}
for(int r=0;r<=2*K;r++)for(int i=0;i<=n;i++)for(int o=0;o<2;o++)B[r][i][o]=0;
}
return 0;
}
```