Link.
Description.
给定 \(m\) 个长度不超过 \(n(n\le 20)\) 的 01
串。
找到字典序最小的字符,使它是至少 \(K\) 个字符串的子序列。
Solution.
首先考虑一个暴力做法,\(dp_{S,T}\) 表示当前答案字符串是 \(T\),待匹配串是 \(S\) 的答案。
每次转移是对 \(T\) 加字符,\(S\) 中找到第一个 \(T\) 插入的字符并把它前面的全都删掉。
发现有个性质,就是 \(|S|+|T|\le n\),通过刚刚转移过程可以得知。
然后空间复杂度就成为了 \(O(2^n\cdot n^2)\)。
\(n^2\) 是因为两个字符串长度都不知,需要两维维护。
考虑怎么把 \(O(n^2)\rightarrow O(n)\)。
发现在开头插入一个 1
,然后每次找到第一个 1
即可。
Coding.
点击查看代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}/*}}}*/
int n,K,nx[1<<21][2],dp[1<<21][21],tn[1<<21];char ch[1<<21];
inline void output(int x,int w) {for(int i=x-1;~i;i--) putchar(((w>>i)&1)^48);putchar('\n');}
int main()
{
read(n,K);for(int i=0;i<=n;i++)//dp[A][l] -> A=S|T,l 是 T 长度,T 是目标串
{scanf("%s",ch);for(int j=0;j<(1<<i);j++) dp[j|(1<<i)][i]=ch[j]^48;}
for(int i=n;~i;i--) for(int j=0;j<(1<<i);j++) for(int k=i;~k;k--) if(dp[(1<<i)|j][k])
{
int v=dp[(1<<i)|j][k],S=j>>k,T=j&((1<<k)-1);if(k) dp[(1<<(i-k))|S][0]+=v;
if(T)
{
int u=k-1;while(!((T>>u)&1)) u--;
dp[(1<<(i-k+u+1))|(S<<(u+1))|(1<<u)|(T&((1<<u)-1))][u]+=v;
}
if((~T)&((1<<k)-1))
{
int u=k-1;while((T>>u)&1) u--;
dp[(1<<(i-k+u+1))|(S<<(u+1))|(T&((1<<u)-1))][u]+=v;
}
}
for(int i=n;~i;i--) for(int j=0;j<(1<<i);j++) if(dp[(1<<i)|j][0]>=K) return output(i,j),0;
}