修改字符串中的
?
字符,使得字符串中不存在连续相同的字母c++
1 <= n <= 100
算法仅包含小写字母和
?
字符数组
很暴力的题目,贪心选取就结束了ui
#define sz(x) ((int)x.size()) class Solution { public: char get(int i, string s){ char pre = i == 0 ? '$' : s[i - 1]; char nxt = i == sz(s) - 1 ? '$' : s[i + 1]; for (int i = 0; i < 26; ++ i) if (i + 'a' != pre && i + 'a' != nxt) return (i + 'a'); return '$'; } string modifyString(string s) { for (int i = 0; i < sz(s); ++ i) if (s[i] == '?') s[i] = get(i, s); return s; } };
给定两个整数数组
nums1
和nums2
。返回全部知足规则的三元组:spa
- \(nums[i] ^2 = nums[j] \times nums[k] \quad i \in [0, nums1.length],\quad j,k \in [0, nums2.length] \quad j \neq k\)
- \(nums[i] ^2 = nums[j] \times nums[k] \quad i \in [0, nums2.length],\quad j,k \in [0, nums1.length] \quad j \neq k\)
1 <= nums1.length, nums2.length <= 1000
1 <= nums1[i], nums2[i] <= 10^5
其实相似于两数之和,无非是 双指针
或者利用 hashmap
(因为 n
的数量级为 1e3
, 因此直接暴力显然是不合适的)指针
那么思路就比较简单了,还有一个坑点在于数组中最大的数字为 1e5
,平方以后没法用 int
表示,因此须要开 long long
。code
对于双指针写法,须要考虑如何处理重复元素,其实是一个简单的组合数学知识,分为两种状况:排序
nums2[j] == nums2[k]
, 由于咱们排序完成,因此表明 [j, k]
区间中,值都是相同的,且知足条件。根据组合数学能够得知,可选组合为 \(\binom{k - j + 1}{2}\)nums2[j] != nums2[k]
,假设 j <= k
,咱们分别往右往左进行扫描,获得区间 [j, tj)
和 (tk, k]
。注意区间的闭开性。而后利用组合数学的乘法原理计算 \((tj - j) \times (k - tk)\) 。再进行指针的跳转而利用 hashmap
的方法则简单许多,再也不赘述。ip
#define SORT(x) sort(x.begin(), x.end()) #define sz(x) ((int)x.size()) using VI = vector<int>; using LL = long long; class Solution { public: int help(VI nums1, VI nums2){ int ans = 0; for (int i = 0; i < sz(nums1); ++ i){ LL sq = 1LL * nums1[i] * nums1[i]; int l = 0, r = sz(nums2) - 1; while (l < r){ LL cur = 1LL * nums2[l] * nums2[r]; if (cur == sq){ if (nums2[l] == nums2[r]){ ans += (r - l + 1) * (r - l) / 2; break; } int tl = l + 1, tr = r - 1; while (tl < r && nums2[tl] == nums2[l]) ++ tl; while (tr > l && nums2[tr] == nums2[r]) -- tr; ans += (tl - l) * (r - tr); l = tl - 1, r = tr + 1; ++ l; } else if (cur < sq) ++ l; else -- r; } } return ans; } int numTriplets(vector<int>& nums1, vector<int>& nums2) { SORT(nums1); SORT(nums2); int res(0); res += help(nums1, nums2); res += help(nums2, nums1); return res; } };
#define SORT(x) sort(x.begin(), x.end()) #define sz(x) ((int)x.size()) using VI = vector<int>; using LL = long long; class Solution { public: unordered_map<LL, int> mp1, mp2; int numTriplets(vector<int>& nums1, vector<int>& nums2) { mp1.clear(), mp2.clear(); for (int x: nums1) ++ mp1[1LL * x * x]; for (int x: nums2) ++ mp2[1LL * x * x]; int ans(0); for (int i = 0; i + 1 < sz(nums1); ++ i){ for (int j = i + 1; j < sz(nums1); ++ j){ ans += mp2[1LL * nums1[i] * nums1[j]]; } } for (int i = 0; i + 1 < sz(nums2); ++ i){ for (int j = i + 1; j < sz(nums2); ++ j){ ans += mp1[1LL * nums2[i] * nums2[j]]; } } return ans; } };
给你一个字符串
s
和一个整数数组cost
,其中cost[i]
是从s
中删除字符i
的代价。leetcode返回使字符串任意相邻两个字母不相同的最小删除成本。
请注意,删除一个字符后,删除其余字符的成本不会改变。
s.length == cost.length
1 <= s.length, cost.length <= 10^5
1 <= cost[i] <= 10^4
s
中只含有小写英文字母
首先,不难想到使用 dp
去解决,重点在于 dp
数组如何表示。 这题咱们能够顺序 dp ,重点在于考虑最后一个字符,所以 dp 数组能够表示为:
dp[i][j] := 字符串前 i 个字符,且结尾字符为 j 且知足任意相邻字符不一样的最小删除开销
则转移有两种方式,第一种是不删当前字符,第二种是删除当前字符。设当前位置为 i
,当前字符为 c
。
对于第一种转移,枚举 dp[i - 1][k]
且 k != c
,dp[i][c] = min(dp[i][c], dp[i - 1][k])
对于第二种转移,枚举 dp[i - 1][k]
,dp[i][k] = min(dp[i][k], dp[i - 1][k])
固然,存在一个更简单的方法,寻找每一段重复区间,保留其中删除代价最大的字符,用滑动窗口解决。
#define chmax(a, b) a = max(a, b); const int rinf = 0xc0c0c0c0; class Solution { public: int minCost(string s, vector<int>& cost) { int n = sz(cost); int idx = 0, ans = 0; while (idx < n){ int sc = idx; int mx = rinf; int tot = 0; while (sc < n && s[sc] == s[idx]) { chmax(mx, cost[sc]); tot += cost[sc++]; } idx = sc; ans += (tot - mx); } return ans; } };
const int MAXN = 1e5 + 50; int dp[MAXN][30]; class Solution { public: int minCost(string s, vector<int>& cost) { int n = s.length(); for (int i = 0; i <= n; i++) for (int k = 0; k < 26; k++) dp[i][k] = -1; for (int k = 0; k < 26; k++) dp[0][k] = 0; for (int i = 1; i <= n; i++){ int c = s[i - 1] - 'a', v = cost[i - 1]; for (int k = 0; k < 26; k++){ if (k == c) continue; if (dp[i - 1][k] == -1) continue; if (dp[i][c] == -1 || dp[i][c] > dp[i - 1][k]) dp[i][c] = dp[i - 1][k]; } for (int k = 0; k < 26; k++){ if (dp[i - 1][k] == -1) continue; if (dp[i][k] == -1 || dp[i][k] > dp[i - 1][k] + v) dp[i][k] = dp[i - 1][k] + v; } } int ans = -1; for (int k = 0; k < 26; k++){ if (dp[n][k] == -1) continue; if (ans == -1 || ans > dp[n][k]) ans = dp[n][k]; } return ans; } };
Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:
- 类型 1:只能由 Alice 遍历。
- 类型 2:只能由 Bob 遍历。
- 类型 3:Alice 和 Bob 均可以遍历。
给你一个数组
edges
,其中edges[i] = [typei, ui, vi]
表示节点ui
和vi
之间存在类型为typei
的双向边。请你在保证图仍可以被 Alice和 Bob 彻底遍历的前提下,找出能够删除的最大边数。若是从任何节点开始,Alice 和 Bob 均可以到达全部其余节点,则认为图是能够彻底遍历的。返回能够删除的最大边数,若是 Alice 和 Bob 没法彻底遍历图,则返回 -1 。
1 <= n <= 10^5
1 <= edges.length <= min(10^5, 3 * n * (n-1) / 2)
edges[i].length == 3
1 <= edges[i][0] <= 3
1 <= edges[i][1] < edges[i][2] <= n
- 全部元组
(typei, ui, vi)
互不相同
首先,这个题目显然是一个图论题。要求每条路径均可以彻底遍历,显然是生成树有关算法。如何保证保留的边最小呢?考虑贪心法。
首先,公用边尽可能保留。咱们能够先利用公用边进行 DSU 的合并。随后,分别对于 Alice
, Bob
再运行生成树算法。保证每一个人均可以遍历那么答案就很是简单了。
namespace dsu1{ vector<int> parent, rank; void initial(int k){ parent.resize(k); rank.resize(k); for (int i = 0; i < k; ++ i) parent[i] = i, rank[i] = 0; } int find(int x){ return parent[x] == x ? x : parent[x] = find(parent[x]); } void to_union(int x, int y){ // x --> to, y --> from int tx = find(x), ty = find(y); if (tx == ty) return; if (rank[tx] > rank[ty]) parent[ty] = tx; else { parent[tx] = ty; if (rank[tx] == rank[ty]) ++ rank[ty]; } } bool is_same(int x, int y){ return find(x) == find(y); } }; namespace dsu2{ vector<int> parent, rank; void initial(int k){ parent.resize(k); rank.resize(k); for (int i = 0; i < k; ++ i) parent[i] = i, rank[i] = 0; } int find(int x){ return parent[x] == x ? x : parent[x] = find(parent[x]); } void to_union(int x, int y){ // x --> to, y --> from int tx = find(x), ty = find(y); if (tx == ty) return; if (rank[tx] > rank[ty]) parent[ty] = tx; else { parent[tx] = ty; if (rank[tx] == rank[ty]) ++ rank[ty]; } } bool is_same(int x, int y){ return find(x) == find(y); } }; namespace dsu3{ vector<int> parent, rank; void initial(int k){ parent.resize(k); rank.resize(k); for (int i = 0; i < k; ++ i) parent[i] = i, rank[i] = 0; } int find(int x){ return parent[x] == x ? x : parent[x] = find(parent[x]); } void to_union(int x, int y){ // x --> to, y --> from int tx = find(x), ty = find(y); if (tx == ty) return; if (rank[tx] > rank[ty]) parent[ty] = tx; else { parent[tx] = ty; if (rank[tx] == rank[ty]) ++ rank[ty]; } } bool is_same(int x, int y){ return find(x) == find(y); } }; const int maxn = 1e5 + 50; class Solution { public: int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) { dsu1::initial(n + 1), dsu2::initial(n + 1), dsu3::initial(n + 1); int cnt1, cnt2, cnt3, need(0), allu(0); cnt1 = cnt2 = cnt3 = n; for (auto &e:edges){ int t = e[0], u = e[1], v = e[2]; if (t == 1 || t == 3){ if (!dsu1::is_same(u, v)){ dsu1::to_union(u, v); -- cnt1; } } if (t == 2 || t == 3){ if (!dsu2::is_same(u, v)){ dsu2::to_union(u, v); -- cnt2; } } if (t == 3){ ++ allu; if (!dsu3::is_same(u, v)){ dsu3::to_union(u, v); -- cnt3; }else ++ need; } } if (cnt1 != 1 || cnt2 != 1) return -1; return sz(edges) - (allu - need + 2 * (cnt3 - 1)); } };