classSolution { public: boolincreasingTriplet(vector<int>& nums){ int n = nums.size(); vector<int> f(n + 1, INT_MAX); // f[len] = x 表示 以 长度为 len 的序列的最小尾元素为x int ans = 1; for (int i = 0; i < n; i ++) { int l = 1, r = i + 1; int x = nums[i]; while (l < r) { // 二分查找小于nums[i]的最小元素 int mid = l + r >> 1; if (f[mid] >= x) r = mid; else l = mid + 1; } f[r] = x; ans = max(ans, r); } return ans >= 3; } };
classSolution { public: intnumberOfArithmeticSlices(vector<int>& a){ typedeflonglong LL; int n = a.size(); // 由于每个数可能都会在不同等差数列中,需要用哈希表来存 vector<unordered_map<LL, int>> f(n); // f[i][j] 表示 考虑以第 i 个数结尾 公差为j的等差数列的个数 int res = 0; for (int i = 0; i < n; i ++) for (int k = 0; k < i; k ++) { LL j = (LL)a[i] - a[k]; auto it = f[k].find(j); // 查找a[k]结尾公差为j的等差数列的个数 int t = 0; if (it != f[k].end()) { t = it->second; res += t; } f[i][j] += t + 1; } return res; } };
typedefunsignedlonglong ULL; unordered_set<ULL> hash; constint P = 131;
vector<string> findAllConcatenatedWordsInADict(vector<string>& words){ // 初始化字符串哈希表 for (auto &s: words) { ULL t = 0; for (auto &c: s) { t = t * P + c; } hash.insert(t); }
vector<string> res; for (auto &s: words) if (check(s)) res.push_back(s); return res; }
boolcheck(string str){ int n = str.size(); vector<int> f(n + 1, -1); // f[i] 表示 在i前面的连接词的个数 f[0] = 0;
for (int i = 0; i <= n; i ++) { if (f[i]== -1) continue; ULL cur = 0; for (int j = i + 1; j <= n; j ++) { cur = cur * P + str[j - 1]; if (hash.count(cur)) { f[j] = max(f[j], f[i] + 1); } } if (f[n] >= 2) returntrue; } returnfalse; } };
classSolution { public: intfindNumberOfLIS(vector<int>& nums){ int n = nums.size(); vector<int> f(n), g(n); // f 表示 以i结尾的最长上升子序列长度,g 表示 以 i 结尾的最长上升子序列的个数
int maxl = 0, cnt = 0; // maxl是最长子序列长度,cnt是子序列个数 for (int i = 0; i < n; i ++) { f[i] = g[i] = 1; for (int j = 0; j < i; j ++) { if (nums[j] < nums[i]) { if (f[i] < f[j] + 1) f[i] = f[j] + 1, g[i] = g[j]; elseif (f[i] == f[j] + 1) g[i] += g[j]; } } if (maxl < f[i]) maxl = f[i], cnt = g[i]; elseif (maxl == f[i]) cnt += g[i]; } return cnt; } };
classSolution { public: constint INF = 0x3f3f3f3f; intminCost(vector<int>& houses, vector<vector<int>>& cost, int n, int m, int t){ // f[i][j][k] 表示 考慮前i个房子,第i个房子粉刷为j,分区数量为k的所有方案中的最小总花费 vector<vector<vector<int>>> f(n + 1, vector<vector<int>>(m + 1, vector<int>(t + 1, 0)));
for (int i = 0; i <= n; i ++) for (int j = 0; j <= m; j ++) f[i][j][0] = INF;
for (int i = 1; i <= n; i ++) { int color = houses[i - 1]; // 当前房子的颜色 for (int j = 1; j <= m; j ++) { for (int k = 1; k <= t; k ++) { if (k > i) { // 如果分区数量大于房子数,不合法 f[i][j][k] = INF; continue; }
if (color != 0) { // 当前房子已经染色 if (color == j) { // 只有与当前颜色相同才能被转移 int tmp = INF; for (int p = 1; p <= m; p ++) { // 与前面颜色不同(可组成分区的情况) if (p != j) { tmp = min(tmp, f[i - 1][p][k - 1]); } } f[i][j][k] = min(f[i - 1][j][k], tmp); // 与前面颜色相同的情况 } else { f[i][j][k] = INF; } } else { // 当前房子未被染色 int u = cost[i - 1][j - 1]; int tmp = INF; for (int p = 1; p <= m; p ++) { // 与前面颜色不同(可组成分区的情况) if (p != j) { tmp = min(tmp, f[i - 1][p][k - 1]); } } f[i][j][k] = min(f[i - 1][j][k], tmp) + u; // 与前面颜色相同的情况 } } } }
int ans = INF; for (int i = 1; i <= m; i ++) ans = min(ans, f[n][i][t]); return ans == INF ? -1: ans; } };
intminOperations(vector<int>& t, vector<int>& a){ int n = t.size(), m = a.size(); unordered_map<int, int> hash; for (int i = 0; i < n; i ++) hash.insert({t[i], i}); // 建立 target 和 arr 的映射关系 vector<int> list; for (auto x: a) { // 由于target各元素不相同,list中保存了arr与target相同元素的下标,并且递增,故可转化为LIS问题 if (hash.count(x)) list.push_back(hash[x]); } int cnt = list.size(); // q[i] 表示 长度为i的上升子序列 中末尾元素最小的数 vector<int> q(cnt + 1, 0); // 使用LIS的贪心+二分的方法求解,复杂度为nlog(n) // 个人感觉这种优化方式主要是维护一个单调队列,每次加入新的元素,要和之前加入的对比, // 找到比自己小的最后一个数,那么它就可以代替这之前的那个数,因为它更小,更好维护递增序列, // 例如 1 3 5 ,加入1个4(规定长度3),那肯定4替换掉5更好 int len = 0; for (int i = 0; i < cnt; i ++) { int l = 0, r = len; while (l < r) { int mid = l + r + 1 >> 1; if (q[mid] < list[i]) l = mid; else r = mid - 1; } q[r + 1] = list[i]; if (r + 1 > len) len ++; } return n - len; } };