Loading

[题解] [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 */
posted @ 2021-09-25 15:33  IrisT  阅读(21)  评论(0编辑  收藏  举报