[SOJ #687]双生串(2019-11-6考试)/[hdu5431]AB String

题目大意

把所有仅包含\(AB\)的字符串按字典序排列,给你一个仅包含\(AB\)的字符串\(S\),然后有\(Q\)个问题,第\(i\)个问题给你\(k_i\),求不是\(S\)的子串中,第\(k_i\)小的是什么。\(T\)组数据

\(T\leqslant5\)\(\sum|S_i|\leqslant2.3\times10^5\)\(Q_i\leqslant10\)\(k_i\leqslant10^9\)

题解

发现,长度为\(l\)的字符串有\(2^l\)个,而\(S\)的长度小于等于\(l\)的子串最多有\(l|S|\)个,可以发现,当\(l>31\)\(2^l-l|S|>10^9\),即答案长度一定小于等于\(31\)。因为字符串中只含有\(AB\),可以用二进制表示,用\(\mathrm{hash}\)算出\(S\)中每种长度的子串。这样可以算出最终的答案的长度,然后在这一个长度中二分即可。

卡点

多测一定要清空!!

C++ Code:

#include <cstdio>
#include <iostream>
#include <algorithm>

int T, n, Q, k, p;
int s[32][250000], cnt[32];
std::string __s;
bool check(int mid, int p) {
	return mid + 1 -
		(std::upper_bound(s[p], s[p] + cnt[p], mid) - s[p]) >= k;
}
int main() {
	std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
	std::cin >> T;
	while (T --> 0) {
		std::cin >> __s, n = __s.length();
		for (int i = 1; i <= 31 && i <= n; ++i) {
			const int I = (1 << i) - 1; s[i][0] = 0;
			for (int j = 0; j < i; ++j) s[i][0] = s[i][0] << 1 | __s[j] - 'A';
			for (int j = i; j < n; ++j)
				s[i][j - i + 1] = (s[i][j - i] << 1 | __s[j] - 'A') & I;
			std::sort(s[i], s[i] + n - i + 1);
			cnt[i] = std::unique(s[i], s[i] + n - i + 1) - s[i];
		}
		std::cin >> Q;
		while (Q --> 0) {
			std::cin >> k;
			for (p = 1; p <= 31; ++p)
				if ((1 << p) - cnt[p] < k) k -= (1 << p) - cnt[p];
				else break;
			int l = 0, r = (1 << p) - 1, ans = -1;
			while (l <= r) {
				int mid = l + r >> 1;
				if (check(mid, p)) ans = mid, r = mid - 1;
				else l = mid + 1;
			}
			for (int i = p - 1; ~i; --i) std::cout << "AB"[ans >> i & 1];
			std::cout.put('\n');
		}
		for (int i = 1; i <= 31; ++i) cnt[i] = 0;
	}
	return 0;
}
posted @ 2019-11-08 07:19  Memory_of_winter  阅读(152)  评论(0编辑  收藏  举报