题目链接
折半搜索
概述
搜索得到一个集合的幂集, 通常集合大小小于等于$40$, 因为$2^{20} \approx 10^6$, 再来上$10$组$20$组数据差不多就到极限了
搜索方法
将集合分成前后两半, 两半分别进行暴搜, 然后合并两部分的结果得到所需答案
题目
题意
给出一个长度为偶数的仅由小写英文字母组成的字符串$S$, 问$S$能不能被分成两个完全相同的子序列
数据范围
多组数据, $T = 21$
$1 \leq |S| \leq 40$
题解
折半搜索, 搜索前一半的所有子序列和后一半的所有子序列
折半后暴搜其实很简单, 但是怎么将两部分的结果合并是个问题, 直接将枚举出的所有子序列合并的话就是$2^{20} \times 2^{20} = 2^{40}$, 和没折半一样
所以我们要想办法进行线性的合并
思路如下:
对于前半段, 我们分解出的两个子序列$lp_1, lp_2$如果是可能的结果子序列的前缀的话,必有$lp_1$是$lp_2$的前缀或者$lp_2$是$lp_1$的前缀, 这是显然的
对于后半段而言, 必有一个是另一个的后缀
那么我们如果把$lp_1, lp_2$中的公共前缀删去, 会有一个变成空字符串(当然也有两个都是空字符串的情况, 此时$lp_1 = lp_2$), 那么另一个非空的字符串, 如果我们在后半段序列中能把这段序列补上, 也就是把后半段的两个子序列删除相同的后缀后也出现了这个非空的字符串, 我们就找到了两个完全相同的子序列(如果$lp_1 = lp_2$, 那么后半段也必须找出相同的两个子序列)
那我们将前半段找到的两个序列删除公共前缀后的序列用map
存一下, 如果在后半段的查找中删除公共后缀的序列出现了map
中存在的序列, 就说明$S$可以被分成两个相同的子序列
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
| #include <iostream> #include <cstdio> #include <cstring> #include <map>
int t; int n; char s[51]; int mid; bool sel[51], lept; std:: map<std:: string, bool> mp; bool judge;
void Dfsl(int now) { if(now > mid) { std:: string tmp[2];
for(int i = 1; i <= mid; i ++) tmp[sel[i]] += s[i];
int l0 = tmp[0].length(), l1 = tmp[1].length(); int ml = std:: min(l0, l1); int pf = ml; for(int i = 0; i < ml; i ++) if(tmp[0][i] != tmp[1][i]) { pf = i;
break; }
if(pf == ml) { if(l0 == l1) lept = true; else if(l0 < l1) { std:: string tp;
for(int i = pf; i < l1; i ++) tp += tmp[1][i];
mp[tp] = true; } else { std:: string tp;
for(int i = pf; i < l0; i ++) tp += tmp[0][i];
mp[tp] = true; } }
return; }
sel[now] = false; Dfsl(now + 1);
sel[now] = true; Dfsl(now + 1); }
void Dfsr(int now) { if(now > n) { std:: string tmp[2];
for(int i = n; i > mid; i --) tmp[sel[i]] += s[i];
int l0 = tmp[0].length(), l1 = tmp[1].length(); int ml = std:: min(l0, l1);
int pf = ml; for(int i = 0; i < ml; i ++) if(tmp[0][i] != tmp[1][i]) { pf = i;
break; }
if(pf == ml) { if(l0 == l1) { if(lept) judge = true; } else if(l0 < l1) { std:: string tp;
for(int i = l1 - 1; i >= pf; i --) tp += tmp[1][i];
if(mp.count(tp)) judge = true; } else { std:: string tp;
for(int i = l0 - 1; i >= pf; i --) tp += tmp[0][i];
if(mp.count(tp)) judge = true; } }
return; }
sel[now] = false; Dfsr(now + 1);
sel[now] = true; Dfsr(now + 1); }
int main() { scanf("%d", &t); while(t --) { lept = false; mp.clear();
scanf("%s", s + 1);
n = strlen(s + 1); mid = n / 2;
Dfsl(1);
judge = false; Dfsr(mid + 1);
if(judge) printf("possible\n"); else printf("impossible\n"); }
return 0; }
|