[题解] [AGC024F] Simple Subsequence Problem
题目大意
有一个 01
串集合 \(S\),其中每个串的长度都不超过 \(N\),你要求出 \(S\) 中至少是 \(K\) 个串的子序列的最长串,如果有多解,输出字典序最小的那组解。
由于 \(S\) 可能很大,因此我们是这样描述 \(S\) 的:
- 你将得到 \((N+1)\) 个
01
串,第 \(i\) 个串的长度为 \(2^{i-1}\)。 - 第 \(i\) 个字符串的第 \(j\) 个字符,代表数字 \((j−1)\) 的、长度为 \((i−1)\) 的二进制表示是否出现在 \(S\) 中。
\(N \leq 20\)。
解题思路
讲题的时候一直讲子序列自动机搞得我以为解法是 YY 一个广义子序列自动机什么的,嘤。
其实是用贪心的思想去匹配,然后记录每种匹配过程的可能性,再压状态。
用 \(f(S_1, S_2)\) 表示已经匹配的子序列是 \(S_1\),还可匹配的串是 \(S_2\) 的情况数。
显然初始是 \(f(\empty,T) = 1\),而每个字符串的答案是 \(f(S,\empty)\)。转移就枚举下一个匹配 \(0/1\) 就好了。
需要注意的是状态要压一压。
考虑记录成 \(f(len,S_1|S_2)\) 即因为 \(|S_1|+|S_2|\le N\),所以把两者存在一起,空间复杂度才对。
只有 \(len\) 是找不到分割点的,不知道 \(0\) 到底是空,还是匹配了一位 \(0\),不过每个串前面补个 \(1\) 就好了。
不仅是题解代码的两倍长,空间复杂度还很高 QAQ
#include <set>
#include <map>
#include <queue>
#include <bitset>
#include <vector>
#include <math.h>
#include <ctype.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
/*
添加 1 应该完全不影响才是()
除了 f 数组的第一维,所有的长度都一定是字符串的实际长度,包括添加的 1 ;
S 和 S1 一定是 1 开头,但是 S2 不一定;
原字符串最长为 n,有 (1 << n + 1) - 1 种
新字符串最长为 n + 1,有 (1 << n + 2) - 1 种
*/
const int N(20);
int n, k;
int f[2][1 << N + 1], g[N + 1][1 << N + 1];
int s[1 << N + 1], len[1 << N + 1];
char ch[1 << N];
struct pal{ int l, S; } nx[N + 2][1 << N + 1][2];
inline void read(int &x){
x = 0; int f = 1, c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)) x = x * 10 + c - 48, c = getchar();
x *= f;
}
inline void out(int len, int S){
if(len < 0) return; if(len == 0){ cerr << "empty\n"; return; }
for(int i(len - 1); ~i; --i) printf("%d", !!(S & (1 << i)));
puts("");
}
void input(){
read(n), read(k);
for(int i(0); i <= n; ++i){
scanf("%s", ch); int m = strlen(ch);
for(int j(0); j < m; ++j)
len[j | (1 << i)] = i + 1, s[j | (1 << i)] = ch[j] - '0';
}
}
void init(){
for(int i(1); i < 1 << n + 1; ++i) if(s[i]) f[0][i] = 1;
nx[0][0][0] = nx[0][0][1] = {-1};
for(int i(1); i <= n + 1; ++i)
for(int j(0); j < 1 << i; ++j){
int p = j >> (i - 1), S = j & (1 << i - 1) - 1;
nx[i][j][p] = {i - 1, S};
nx[i][j][p ^ 1] = nx[i - 1][S][p ^ 1];
}
}
void match(){
for(int i(0); i <= n; ++i){
int p = i & 1; memset(f[!p], 0, sizeof f[p]);
for(int j(1); j < 1 << n + 1; ++j){
int S = j; if(!f[p][S]) continue; pal nw;
int S1, S2, l, l1, l2; l = len[S], l1 = i + 1, l2 = l - l1;
S1 = S >> l2, S2 = S & (1 << l2) - 1;
nw = nx[l2][S2][0];
if(nw.l != -1) f[!p][((S1 << 1) << nw.l) | nw.S] += f[p][S];
nw = nx[l2][S2][1];
if(nw.l != -1) f[!p][((S1 << 1 | 1) << nw.l) | nw.S] += f[p][S];
g[i][S1] += f[p][S];
}
}
}
void output(){
for(int i(n); ~i; --i)
for(int j(0); j < 1 << i; ++j)
if(g[i][j | (1 << i)] >= k) return out(i, j);
}
int main(){
input();
init();
match();
output();
return 0;
}
/* Hemerocallis */