蓝桥杯 5.字符串

news/2025/2/28 18:34:44/

蓝桥杯 5.字符串

文章目录

  • 蓝桥杯 5.字符串
    • KMP&字符串哈希
    • Manacher
    • 编程138-148
    • 字典树基础
    • 01Trie
    • 编程149-155

KMP&字符串哈希

KMP算法

  • 字符串匹配算法, 用于匹配**模式串P(短)文本串S(长)**中出现的所有位置, 例如, S = “ababac”, P = “aba”, 那么出现的所有位置就是1和3
  • 初学KMP时, 只需记住和学会使用模板即可, 对其原理不需要过度深究
  • KMP算法将原本O(n^2)的字符串匹配算法优化到了O(n), 其中精髓在于next数组(仅与模式串P有关), next数组表示此时模式串下标失配时应该移动到的位置, 也表示最长的相同真前后缀的长度
// 计算next数组的方式就是用P自己去匹配自己, 只需要掌握模板即可
char s[N], p[M];
int nex[M];
int n = strlen(s + 1), m = strlen(p + 1);
nex[0] = nex[1] = 0; // 初始化
for(int i = 2, j = 0; i <= m; i++){// 不断匹配p[i]和p[j + 1]while(j && p[i] != p[j + 1]) j = nex[j]; // 失配if(p[i] == p[j + 1]) j++; // 从while出来后要么j = 0, 要么p[i] == p[j+1] , 如果匹配成功, 则j后移nex[i] = j; // i如果匹配失败就回到j, 因为此时p[1~j]=p[i-j+1~i]或j=0(回到最初的地方重新开始匹配)
}// 通过next数组进行匹配
for(int i = 1, j = 0; i <= n; i++){while(j && s[i] != p[j + 1]) j = nex[j]; // 失配if(s[i] == p[j + 1]) j++;if(j == m) // 成功匹配一次
}

例题讲解

image-20250224175120346

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e5 + 5;
ll a[N];
char s[N], p[N];int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> p >> s;// 计算next数组的方式就是用P自己去匹配自己, 只需要掌握模板即可int nex[N];int n = strlen(s + 1), m = strlen(p + 1);nex[0] = nex[1] = 0; // 初始化for(int i = 2, j = 0; i <= m; i++){// 不断匹配p[i]和p[j + 1]while(j && p[i] != p[j + 1]) j = nex[j]; // 失配if(p[i] == p[j + 1]) j++; // 从while出来后要么j = 0, 要么p[i] == p[j+1] , 如果匹配成功, 则j后移nex[i] = j; // i如果匹配失败就回到j, 因为此时p[1~j]=p[i-j+1~i]或j=0(回到最初的地方重新开始匹配)}ll ans = 0;// 通过next数组进行匹配for(int i = 1, j = 0; i <= n; i++){while(j && s[i] != p[j + 1]) j = nex[j]; // 失配if(s[i] == p[j + 1]) j++;if(j == m) ans++; // 成功匹配一次}cout << ans << "\n";return 0;
}

字符串哈希

  • (基于进制的)字符串Hash本质上是用一个数字表示一段字符串, 从而降低字符串处理的复杂度
  • 这个数字是一个很大的数字, 采用unsigned int 类型, 可以自然溢出, 从而简化了去模的部分
  • 还需要一个进制数base, 用于规定这个数字的进制
  • Hash数组h[i]表示字符串s[1~i]的Hash值, 采用类似前缀和的形式以便于求出任意一个子串的Hash值
// 字符串Hash初始化:
typedef unsigned long long ull;
const ull base = 131; // base一般为一个质数
ull h[N];
char s[N];
for(int i = 1; i <= n; i++) h[i] = h[i - 1] * base + s[i] - 'A' + 1;// 获取子串s[l]~s[r]的Hash:
ull getHash(int l, int r){return h[r] - h[l - 1] * b[r - l + 1]; // b[i]表示base的i次方(预处理)
}

例题讲解

  • 还是上一道题, 1.斤斤计较的小Z
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
typedef unsigned long long ull;
const ull base = 131; // base一般为一个质数
ull h1[N], h2[N], b[N];
char s[N], p[N];// 获取子串s[l]~s[r]的Hash:
ull getHash(ull h[], int l, int r){return h[r] - h[l - 1] * b[r - l + 1]; // b[i]表示base的i次方(预处理)
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> p >> s;int nex[N];int n = strlen(s + 1), m = strlen(p + 1);b[0] = 1;// 字符串Hash初始化:for(int i = 1; i <= n; i++) {b[i] = b[i - 1] * base;h1[i] = h1[i - 1] * base + (int)p[i];h2[i] = h2[i - 1] * base + (int)s[i];}int ans = 0;for(int i = 1; i + m - 1 <= n; i++){if(getHash(h1, 1, m) == getHash(h2, i, i + m - 1)) ans++;}cout << ans << "\n";return 0;
}

Manacher

回文串的性质

  • 类似AABAA, 对于每个i具有s[i] = s[n + 1 - i]的字符串
  • 回文半径: 对于一个回文中心i, 如果他的半径为r, 如果他为奇数长度的回文串的中心, 则说明[i - r + 1, i + r - 1]为一个回文串, 如果i是偶数长度的回文中心, 则回文半径没有意义(Manacher算法会解决这个问题)
  • 回文的递归性质: 某个点的回文半径相比关于某个回文中心的点的回文半径一定相等或更大

Manacher算法

  • ABBA没有回文半径, Manacher就是将所有的回文串转换成奇数长度的回文串, 方法是在字符串中间和头尾插入特殊字符, 转换后变成了^#A#B**#B#A$, 这个回文串是以黑色#**为回文中心, 以5为回文半径的, 它就表示原回文串中的回文长度为5-1=4
  • 位置i的回文半径以p[i]表示, 意思是在转换后的字符串中[i-p[i]+1, i+p[i]-1]是回文的

image-20250224203718876

image-20250224204600117

int c = 0, r = 0; // 初试都从0开始
for(int i = 1; i <= 2 * n + 1; i++){p[i] = i < r ? min(r - i, p[2 * c - i]) : 1;// 如果p[i]可以更大, 就持续更新while(s[i + p[i]] == s[i - p[i]]) p[i]++;// 如果此时i可以作为一个更好的c, 就替换if(i + p[i] > r) c = i, r = i + p[i];
}

例题讲解

image-20250224213138138

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e6 + 5;
int p[N];
char s[N];int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> s + 1;int c = 0, r = 0; // 初试都从0开始int n = strlen(s + 1);// 添加符号, 使其成为Manacher字符串for(int i = 2 * n + 1; i >= 1; i--){s[i] = (i & 1) ? '#' : s[i >> 1];}s[0] = '^', s[2 * n + 1] = '$';for(int i = 1; i <= 2 * n + 1; i++){p[i] = i < r ? min(r - i, p[2 * c - i]) : 1;// 如果p[i]可以更大, 就持续更新while(s[i + p[i]] == s[i - p[i]]) p[i]++;// 如果此时i可以作为一个更好的c, 就替换if(i + p[i] > r) c = i, r = i + p[i];}int ans = 0;for(int i = 1; i <= 2 * n + 1; i++){ans = max(ans, p[i] - 1);}cout << ans << "\n";return 0;
}

编程138-148

image-20250224224252879

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 2e5 + 5;
string p;
int nex[N];void getNext(string &p){// 计算next数组的方式就是用P自己去匹配自己, 只需要掌握模板即可nex[0] = 0; // 初始化for(int i = 1, j = 0; i < p.size(); i++){// 不断匹配p[i]和p[j + 1]while(j > 0 && p[i] != p[j]) j = nex[j - 1]; // 失配if(p[i] == p[j]) j++; // 从while出来后要么j = 0, 要么p[i] == p[j+1] , 如果匹配成功, 则j后移nex[i] = j; // i如果匹配失败就回到j, 因为此时p[1~j]=p[i-j+1~i]或j=0(回到最初的地方重新开始匹配)}
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n; cin >> n;cin >> p;int ans = 0;getNext(p);for(int i = 0; i < p.size(); i++){ans = max(ans, nex[i]);}cout << ans << "\n";return 0;
}

image-20250225083049199

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e6 + 5;
string p;
ll nex[N], ans;void getNext(string &p){// 计算next数组的方式就是用P自己去匹配自己, 只需要掌握模板即可int j = 0;nex[0] = 0; // 初始化for(int i = 1; i < p.size(); i++){// 不断匹配p[i]和p[j + 1]while(j > 0 && p[i] != p[j]) j = nex[j - 1]; // 失配if(p[i] == p[j]) j++; // 从while出来后要么j = 0, 要么p[i] == p[j+1] , 如果匹配成功, 则j后移nex[i] = j; // i如果匹配失败就回到j, 因为此时p[1~j]=p[i-j+1~i]或j=0(回到最初的地方重新开始匹配)}
}ll find(ll x){if(!nex[x]) return x + 1;else return nex[x] = find(nex[x] - 1);
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n; cin >> n >> p;getNext(p);for(int i = 0; i < p.size(); i++){if(nex[i]) ans += (i + 1) - find(i);}cout << ans << "\n";return 0;
}

image-20250225092804059

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 2e5 + 5;
string p, t;
ll nex[N], ans;
vector<ll> pre(N + 2), suf(N + 1);void getNext(string &p){// 计算next数组的方式就是用P自己去匹配自己, 只需要掌握模板即可int j = 0;nex[0] = 0; // 初始化for(int i = 1; i < p.size(); i++){// 不断匹配p[i]和p[j + 1]while(j > 0 && p[i] != p[j]) j = nex[j - 1]; // 失配if(p[i] == p[j]) j++; // 从while出来后要么j = 0, 要么p[i] == p[j+1] , 如果匹配成功, 则j后移nex[i] = j; // i如果匹配失败就回到j, 因为此时p[1~j]=p[i-j+1~i]或j=0(回到最初的地方重新开始匹配)}
}// 通过next数组进行匹配
int KMP(string p, string s, vector<ll> &f){int a = -1;memset(nex, 0, sizeof(nex));getNext(s);for(int i = 0, j = 0; i < p.size(); i++){while(j && s[j] != p[i]) j = nex[j - 1]; // 失配if(s[j] == p[i]) j++;f[i] = j;if(j == s.size()) j = nex[j - 1]; // 成功匹配一次}return a;
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n, m, k; cin >> n >> m >> k >> p >> t;KMP(p, t, pre);reverse(p.begin(), p.end());reverse(t.begin(), t.end());KMP(p, t, suf);reverse(suf.begin(), suf.begin() + n);map<ll, ll> mp;for(int i = 0; i < n; i++){mp[suf[i]]++;}for(int i = 0; i < n; i++){mp[suf[i]]--;if(mp[m - pre[i]]){cout << "Yes\n";return 0;}}cout << "No\n";return 0;
}

image-20250225093617100

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e6 + 5;
string p, t;
ll nex[N], ans;void getNext(string &p){// 计算next数组的方式就是用P自己去匹配自己, 只需要掌握模板即可int j = 0;nex[0] = 0; // 初始化for(int i = 1; i < p.size(); i++){// 不断匹配p[i]和p[j + 1]while(j > 0 && p[i] != p[j]) j = nex[j - 1]; // 失配if(p[i] == p[j]) j++; // 从while出来后要么j = 0, 要么p[i] == p[j+1] , 如果匹配成功, 则j后移nex[i] = j; // i如果匹配失败就回到j, 因为此时p[1~j]=p[i-j+1~i]或j=0(回到最初的地方重新开始匹配)}
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> p;getNext(p);ll tmp = p.size() - nex[p.size() - 1]; // 最小周期if(tmp != p.size() && p.size() % tmp == 0){ // 必须是周期的整数倍(不是的话就不能拼接成完整的字符串, 所以为1), 且和周期不相等, 相等则为1cout << p.size() / tmp; // 总长度/最小周期 == 最多子串的数量} else {cout << 1;}return 0;
}

142诗歌双联之谜 跟上面的诗歌双联一样, 而且比上面的题还弱了

image-20250225112351780

image-20250225095728817

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e6 + 5;
const ll INF = 1e14 + 9;
string s, p;
// char s[N], p[N];
ll nex[N];int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n; cin >> n >> s >> p;s += s;s = " " + s;p = " " + p;ll m = n; n <<= 1;for(int i = 1; i <= n; i++){if(islower(s[i])){s[i] = toupper(s[i]);} else {s[i] = tolower(s[i]);}}nex[0] = 0; // 初始化for(int i = 2, j = 0; i <= m; i++){// 不断匹配p[i]和p[j + 1]while(j && p[i] != p[j + 1]) j = nex[j]; // 失配if(p[i] == p[j + 1]) j++; // 从while出来后要么j = 0, 要么p[i] == p[j+1] , 如果匹配成功, 则j后移nex[i] = j; // i如果匹配失败就回到j, 因为此时p[1~j]=p[i-j+1~i]或j=0(回到最初的地方重新开始匹配)}ll ans = INF;for(int i = 1, j = 0; i <= n; i++){while(j && s[i] != p[j + 1]) j = nex[j]; // 失配if(s[i] == p[j + 1]) j++;if(j == m) {// 成功匹配一次ans = min({ans, i - m, 2 * m - i});// ans = min(ans, i - m);}}if(ans == INF) cout << "No\n";else cout << "Yes\n" << ans << "\n";return 0;
}

image-20250225131849521

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 2e5 + 5;
string p;
ll nex[N], f[N];void getNext(string &p){// 计算next数组的方式就是用P自己去匹配自己, 只需要掌握模板即可nex[0] = 0; // 初始化for(int i = 1, j = 0; i < p.size(); i++){// 不断匹配p[i]和p[j + 1]while(j > 0 && p[i] != p[j]) j = nex[j - 1]; // 失配if(p[i] == p[j]) j++; // 从while出来后要么j = 0, 要么p[i] == p[j+1] , 如果匹配成功, 则j后移nex[i] = j; // i如果匹配失败就回到j, 因为此时p[1~j]=p[i-j+1~i]或j=0(回到最初的地方重新开始匹配)}
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n; cin >> n >> p;ll ans = 0;getNext(p);for(int i = 0; i < n; i++){f[i] = 1;}for(int i = n; i >= 0; i--){f[nex[i] - 1] += f[i];}for(int i = 0; i < n; i++){ans += f[i];}cout << ans << "\n";return 0;
}

image-20250225145916491

#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int N = 200010;
const int base = 131; // base一般为一个质数
ull h1[N], h2[N], p[N];
int n;
char s[N];// 获取子串s[l]~s[r]的Hash:
ull getHash1(int l, int r){return h1[r] - h1[l - 1] * p[r - l + 1];
}
ull getHash2(int l, int r){l = n - l + 1, r = n - r + 1;return h2[r] - h2[l - 1] * p[r - l + 1]; // 得到反转字符串的Hash值
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> s + 1;n = strlen(s + 1) * 2 + 1;// 添加符号, 使其成为Manacher字符串for(int i = n - 1; i; i -= 2){s[i + 1] = 'z' + 1;s[i] = s[i / 2];}s[1] = 'z' + 1;p[0] = 1;for(int i = 1, j = n; i <= n; i++, j--){ // 处理Hashh1[i] = h1[i - 1] * base + s[i] - 'a' + 1;h2[i] = h2[i - 1] * base + s[j] - 'a' + 1;p[i] = p[i - 1] * base;}int ans = 0;for(int i = 1; i <= n; i++){int l = 0, r = min(i - 1, n - i);while(l < r){int mid = l + r + 1 >> 1;if(getHash1(i - mid, i - 1) != getHash2(i + mid, i + 1)) {r = mid - 1;} else {l = mid;}}int len = i - l - 1;if(getHash1(1, len) == getHash2(n, n - len + 1)){if(s[i - l] <= 'z') ans = max(ans, l + 1 + len / 2 * 2);else ans = max(ans, l + len / 2 * 2);}len = n - (i + l);if(getHash1(1, len) == getHash2(n, n - len + 1)){if(s[i - l] <= 'z') ans = max(ans, l + 1 + len / 2 * 2);else ans = max(ans, l + len / 2 * 2);}}cout << ans << "\n";return 0;
}

146-148没写

字典树基础

朴素字符串查找

  • 给n个字符串s1~sn, 再给1个字符串t, 如何快速查找t是否在s中出现过
  • 朴素的方法是循环遍历s1~sn, 并比较si是否和t相等即可, 但是这个时间复杂度会比较大, 可能会超时

字典树

  • 字典树也叫Trie树, 是一种树形结构, 其中每个节点可以存储(当然也可以不存储)一些变量用于表示该字符串的数量, 下图中节点上的数字表示结点编号, 每条边表示一个字符
  • 建立一棵这样的树
int nex[N][27]; // nex[i][0]表示从结点i出发, 边为a的下一个结点地址
int cnt[N]; // cnt[i]表示以结点i结尾的字符串数量
int idx = 2; // 内存池, 用于动态开点
// 初始时, 只有一个结点1(作为根节点)
  • 编写insert函数, 用于将一个字符串s加入进去
void insert(char s[]){ // 长度为n的字符串s插入到trie中int n = strlen(s + 1);int x = 1; // 从1开始往下走for(int i = 1; i <= n; i++){// 先检查x是否存在s[i]的边if(!nex[x][s[i] - 'a']) nex[x][s[i] - 'a'] = idx++;x = nex[x][s[i] - 'a']; // x移动到新点上}cnt[x]++;
}
  • 编写一个check函数, 用于判断某个字符串在trie中的出现的次数
int check(char s[]){int n = strlen(s + 1);int x = 1;for(int i = 1; i <= n; i++) x = nex[x][s[i] - 'a'];return cnt[x];
}

image-20250225150840886

例题讲解

image-20250225194822820

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 2e6 + 5;int nex[N][26];
int cnt[N];
int idx = 2;void insert(char s[]){ // 长度为n的字符串s插入到trie中int x = 1; // 从1开始往下走for(int i = 1; s[i]; i++){// 先检查x是否存在s[i]的边if(!nex[x][s[i] - 'a']) nex[x][s[i] - 'a'] = idx++;x = nex[x][s[i] - 'a']; // x移动到新点上}cnt[x]++;
}bool check(char s[]){int x = 1;for(int i = 0; s[i]; i++) x = nex[x][s[i] - 'a'];return x;
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n, m; cin >> n >> m;for(int i = 1; i <= n; i++){char s[N]; cin >> s + 1;insert(s);}for(int y = 1; y <= m; y++){char t[N]; cin >> t;cout << (check(t) ? "Y" : "N") << "\n";}return 0;
}

01Trie

介绍

image-20250225195251885

image-20250225195724410

插入

image-20250225200024615

查询

image-20250225200834947

例题讲解

image-20250225202143690

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 32 * 1e5 + 6, M = 1e5 + 9;
int a[M], prefix[M];
int son[N][2], tot = 1;void insert(int x){int o = 1;for(int i = 30; i >= 0; i--){int y = x >> i & 1;if(!son[o][y]) son[o][y] = ++tot;o = son[o][y];}
}
int query(int x){int o = 1, res = 0;for(int i = 30; i >= 0; i--){int y = x >> i & 1;if(son[o][!y]) o = son[o][!y], res |= (1 << i);else o = son[o][y];}return res;
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n; cin >> n;for(int i = 1; i <= n; i++){cin >> a[i];}for(int i = 1; i <= n; i++){prefix[i] = prefix[i - 1] ^ a[i];}insert(0);int ans = 0;for(int i = 1; i <= n; i++){ans = max(ans, query(prefix[i]));insert(prefix[i]);}cout << ans << "\n";return 0;
}

编程149-155

image-20250226194440596

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e3 + 5;
string s;
map<string, ll> mp;int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n; cin >> n;for(int i = 1; i <= n; i++){cin >> s;mp[s]++;}for(auto it : mp){if(it.second >= 2){cout << 1 << "\n";return 0;}}cout << 0 << "\n";return 0;
}

image-20250226201953849

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 1e5 + 5;
string s[N];
int nex[N][27]; // nex[i][0]表示从结点i出发, 边为a的下一个结点地址
int cnt[N]; // cnt[i]表示以结点i结尾的字符串数量
int idx = 0; // 内存池, 用于动态开点
// 初始时, 只有一个结点1(作为根节点)void insert(string &s){ // 长度为n的字符串s插入到trie中int n = s.length();int x = 0; // 从1开始往下走for(int i = 0; i < n; i++){// 先检查x是否存在s[i]的边if(!nex[x][s[i] - 'a']) nex[x][s[i] - 'a'] = ++idx;x = nex[x][s[i] - 'a']; // x移动到新点上cnt[x]++;}}int check(string &s){int n = s.length();int x = 0, dep = 0, mx = 0;for(int i = 0; i < n; i++){x = nex[x][s[i] - 'a'];dep++;if(cnt[x] >= 2){mx = max(mx, dep);}} return mx;
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n; cin >> n;for(int i = 1; i <= n; i++){cin >> s[i];insert(s[i]);}for(int i = 1; i <= n; i++){cout << check(s[i]) << "\n";}return 0;
}

image-20250226203331654

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 2e5 + 5;
string s[N];
string t[N];
int nex[N][27]; // nex[i][0]表示从结点i出发, 边为a的下一个结点地址
int cnt[N]; // cnt[i]表示以结点i结尾的字符串数量
int idx = 0; // 内存池, 用于动态开点
// 初始时, 只有一个结点1(作为根节点)void insert(string &s){ // 长度为n的字符串s插入到trie中int n = s.length();int x = 0; // 从1开始往下走for(int i = 0; i < n; i++){// 先检查x是否存在s[i]的边if(!nex[x][s[i] - 'a']) nex[x][s[i] - 'a'] = ++idx;x = nex[x][s[i] - 'a']; // x移动到新点上cnt[x]++;}}int check(string &s){int n = s.length();int x = 0, dep = 0, mx = 0;for(int i = 0; i < n; i++){if(nex[x][s[i] - 'a']){x = nex[x][s[i] - 'a']; // 能走就走} else {return 0; // 走不了则0}} return cnt[x];}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n, m; cin >> n >> m;for(int i = 1; i <= n; i++){cin >> s[i];insert(s[i]);}for(int i = 1; i <= m; i++){cin >> t[i];cout << check(t[i]) << "\n";}return 0;
}

image-20250226210303571

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll N = 32 * 1e5 + 6, M = 1e5 + 9;
int a[M], prefix[M];
int son[N][2], tot = 1;void insert(int x){int o = 1;for(int i = 30; i >= 0; i--){int y = x >> i & 1;if(!son[o][y]) son[o][y] = ++tot;o = son[o][y];}
}
int query(int x){int o = 1, res = 0;for(int i = 30; i >= 0; i--){int y = x >> i & 1;if(son[o][!y]) o = son[o][!y], res |= (1 << i);else o = son[o][y];}return res;
}int main(){ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);ll n; cin >> n;for(int i = 1; i <= n; i++){cin >> a[i];insert(a[i]);}ll q; cin >> q;for(int i = 1; i <= q; i++){ll x; cin >> x;cout << query(x) << "\n";}return 0;
}

153-155没写


http://www.ppmy.cn/news/1575568.html

相关文章

Imagination通过最新的D系列GPU IP将效率提升至新高度

Imagination DXTP GPU IP在加速移动设备和其他电力受限设备上的图形和计算工作负载时&#xff0c;能够延长电池续航时间。 英国伦敦 – 2025年2月25日 – 今日&#xff0c;Imagination Technologies&#xff08;“Imagination”&#xff09;宣布推出其最新的GPU IP——Imagina…

NTS库学习,找bug中......

基础知识 Coordinate: 表示一个二维坐标点&#xff0c;包括 X 和 Y 坐标值。 CoordinateSequence: 由一系列 Coordinate 对象组成的序列&#xff0c;可以表示线、多边形等几何对象的顶点。 CoordinateFilter: 用于对几何对象的坐标进行过滤或修改的接口。 Geometry: 表示一个几…

QT 中的元对象系统(二):元对象实现原理QMetaObject

目录 1.元对象系统的构成 2.QObject和QMetaObject的关系 3.Qt 元对象模型QMetaObject 3.1.基本信息 3.2.类信息classinfo 3.3.类构造函数constructor 3.4.枚举信息 enumerator 3.5.类方法method 3.6.类属性peoproty 4.MOS(Meta Object System)示例 5.总结 1.元对象系…

端口映射/内网穿透方式及问题解决:warning: remote port forwarding failed for listen port

文章目录 需求&#xff1a;A机器是内网机器&#xff0c;B机器是公网服务器&#xff0c;想要从公网&#xff0c;访问A机器的端口方式&#xff1a;端口映射&#xff0c;内网穿透&#xff0c;使用ssh打洞端口&#xff1a;遇到问题&#xff1a;命令执行成功&#xff0c;但是端口转发…

SSL 证书是 SSL 协议实现安全通信的必要组成部分

SSL证书和SSL/TLS协议有着密切的关系&#xff0c;但它们本质上是不同的概念。下面是两者的区别和它们之间的关系的表格&#xff1a; 属性SSL/TLS 协议SSL证书英文全称SSL&#xff08;Secure Sockets Layer&#xff09;&#xff0c;TLS&#xff08;Transport Layer Security&am…

Cherry Studio + 火山引擎 构建个人AI智能知识库

&#x1f349;在信息化时代&#xff0c;个人知识库的构建对于提高工作效率、知识管理和信息提取尤为重要。尤其是当这些知识库能结合人工智能来智能化地整理、分类和管理数据时&#xff0c;效果更为显著。我最近尝试通过 Cherry Studio 和 火山引擎 来搭建个人智能知识库&#…

C#上位机--二级运算符

序言 在 C# 编程领域&#xff0c;尤其是针对上位机开发时&#xff0c;二元运算符是构建复杂逻辑和高效代码的基础工具之一。它们通过对两个操作数进行运算&#xff0c;为开发者提供了丰富的数据处理手段。接下来&#xff0c;我们将深入探讨 C# 上位机中的二元运算符&#xff0…

计算机网络实验2-虚拟局域网(VLAN)划分

四、实验内容 1、根据实验拓扑图建立好实验环境&#xff0c;并设定 PC 机的 IP 地址&#xff1b;并通过其中一台 PC 机 ping 其余 主机&#xff0c;查看 ping 命令运行结果。 2、通过交换机的基本命令&#xff0c;在 S5700 交换机上创建虚拟局域网&#xff08;VLAN&#xff09;…