/** z[i] 表示 s[i:] 与 s 的最长公共前缀长度 */ vector<int> zFunc(string s){ int n = s.size(); vector<int> z(n, 0); int l = 0, r = 0; for (int i = 1; i < n; i++) { if (i <= r && z[i - l] < r - i + 1) { z[i] = z[i - l]; } else { z[i] = max(0, r - i + 1); while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { z[i]++; } } if (i + z[i] - 1 > r) { l = i; r = i + z[i] - 1; } } return z; }
// version 2: vector<int> zFunc(string s){ int n = s.size(); vector<int> z(n, 0); int l = 0, r = 0; for (int i = 1; i < n; i++) { if (i <= r) { z[i] = min(z[i - l], r - i + 1); } while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { l = i; r = i + z[i]; z[i]++; } } return z; }
classSolution { public: intcountSpecialNumbers(int n){ auto s = to_string(n); int m = s.length(), dp[m][1 << 10]; memset(dp, -1, sizeof(dp)); // mask 表示当前已经使用的数字,is_num 表示是否填充数字,is_limit 表示当前数位是否受上一数位限制 // is_num 用于限制前导零 function<int(int, int, bool, bool)> f = [&](int i, int mask, bool is_limit, bool is_num) -> int { if (i == m) { return is_num; } if (!is_limit && is_num && dp[i][mask] >= 0) { return dp[i][mask]; } int res = 0; // 可以跳过当前数位 if (!is_num) { res = f(i + 1, mask, false, false); } // 枚举要填入的数字 d int up = is_limit ? s[i] - '0' : 9; for (int d = 1 - is_num; d <= up; ++d) { // d 不在 mask 中 (非必要条件) if ((mask >> d & 1) == 0) { res += f(i + 1, mask | (1 << d), is_limit && d == up, true); } } if (!is_limit && is_num) { dp[i][mask] = res; } return res; }; returnf(0, 0, true, false); } };
位运算
HighBit
返回该数对应二进制的最高位 1 所对应的数
1 2 3 4 5 6 7 8
inthighbit(int i){ i |= (i >> 1); i |= (i >> 2); i |= (i >> 4); i |= (i >> 8); i |= (i >> 16); return i - (i >>> 1); }
LowBit
返回该数对应二进制的最低位 1 所对应的数
1 2 3
intlowbit(int i){ return i & -i; }
二进制移除最低位
每次循环将数字 k 的最低二进制为 1 的比特位置为 0
1 2 3
for (; k; k &= k - 1) { // code... }
二进制子集遍历
用于遍历数字 k 的所有二进制子集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// version 1: int sub = k; do { // code... sub = (sub - 1) & k; } while(sub != k);
// version 2: int sub = k; while (sub > 0) { // code... sub = (sub - 1) & k; }
// version 3: for (int sub = s; sub; sub = (sub - 1) & s) { // code... }
C++ 位运算内置函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// 返回 x 的二进制表示中的 1 的个数 int __builtin_popcount(unsignedint x); int __builtin_popcountl(unsignedlong x); int __builtin_popcountll(unsignedlonglong x); // 返回 x 的二进制表示中的 1 的个数奇偶性,偶数个 1 返回 0,反之 1 int __builtin_parity(unsignedint x); int __builtin_parityl(unsignedlong x); int __builtin_parityll(unsignedlonglong x); // 返回 x 的二进制表示中前导的 0 的个数,常用 32 - __builtin_clz(x) 计算 x 对应二进制的有效位数 int __builtin_clz(unsignedint x); int __builtin_clzl(unsignedlong x); int __builtin_clzll(unsignedlonglong x); // 返回 x 的二进制表示中末尾的 0 的个数,不常用 int __builtin_ctz(unsignedint x); int __builtin_ctzl(unsignedlong x); int __builtin_ctzll(unsignedlonglong x);
classTreeAncestor { private: // pa[i][j] 表示节点 i 的第 2^j 个祖先 vector<vector<int>> pa; public: TreeAncestor(int n, vector<int>& parent) { int m = 32 - __builtin_clz(n); pa.resize(n, vector<int>(m, -1)); // 预处理每个节点的第 1 个祖先 for (int i = 0; i < n; i++) { pa[i][0] = parent[i]; } // pa[x][i + 1] = pa[pa[x][i]][i] // 即表示 x 的第 2^i 个祖先节点的第 2^i 个祖先节点就是 x 的第 2^(i+1) 个祖先节点。 for (int i = 0; i < m - 1; i++) { for (int j = 0; j < n; j++) { int p = pa[j][i]; if (p != -1) { pa[j][i + 1] = pa[p][i]; } } } }
intgetKthAncestor(int node, int k){ int m = 32 - __builtin_clz(k); // 二进制分解 k 进行 log(k) 次跳跃查询 for (int i = 0; i < m; i++) { if ((k >> i) & 1) { node = pa[node][i]; if (node < 0) { break; } } } return node; } };
【LCA】最近公共祖先
朴素的最近公共祖先可以通过 DFS 进行查询,时间复杂度为 \(O(n)\)。
基于倍增的解法是首先预处理所有节点的第 \(2^i\) 个祖先,然后将待求解的节点 x 和 y
置于同一层,进行 \(log_{2}{k}\)
次祖先查询,最终得到公共祖先,其中 k 为树的最大高度。
// 倍增预处理出每个节点的第 2^i 个祖先 for (int i = 0; i < m - 1; i++) { for (int x = 0; x < n; x++) { int p = pa[x][i]; if (p != -1) { pa[x][i + 1] = pa[p][i]; } } } }
// 获取节点 node 的第 k 个祖先 intget_kth_ancestor(int node, int k){ for (; k; k &= k - 1) node = pa[node][__builtin_ctz(k)]; return node; }
// 返回 x 和 y 的最近公共祖先(节点编号从 0 开始) intget_lca(int x, int y){ if (depth[x] > depth[y]) swap(x, y); // 使 y 和 x 在同一深度 y = get_kth_ancestor(y, depth[y] - depth[x]); if (y == x) return x; // 循环从大到小,尝试每次上跳 i 步,理解为二进制分解两个节点的公共祖先高度 for (int i = pa[x].size() - 1; i >= 0; i--) { int px = pa[x][i], py = pa[y][i]; // 跳跃 if (px != py) { x = px; y = py; } } // 保证两个节点最终跳跃的位置对应的父节点为最近公共祖先 return pa[x][0]; } };
public: RangeFreqQuery(vector<int>& arr) { for (int i = 0; i < arr.size(); i++) { um[arr[i]].emplace_back(i); } }
intquery(int left, int right, int value){ if (um.find(value) == um.end()) { return0; } auto l = lower_bound(um[value].begin(), um[value].end(), left); auto r = upper_bound(um[value].begin(), um[value].end(), right); return r - l; } };