第一题
LC 1779. 找到最近的有相同 X 或 Y 坐标的点
难度:简单
算法:数组,扫描
题目描述:
给你两个整数 x 和 y ,表示你在一个笛卡尔坐标系下的 (x, y) 处。同时,在同一个坐标系下给你一个数组 points ,其中 points[i] = [ai, bi] 表示在 (ai, bi) 处有一个点。当一个点与你所在的位置有相同的 x 坐标或者相同的 y 坐标时,我们称这个点是有效的 。
请返回距离你当前位置 曼哈顿距离 最近的 有效 点的下标(下标从 0 开始)。如果有多个最近的有效点,请返回下标 最小 的一个。如果没有有效点,请返回 -1 。
两个点 (x1, y1) 和 (x2, y2) 之间的 曼哈顿距离 为 abs(x1 - x2) + abs(y1 - y2) 。
提示:
1 <= points.length <= 104
points[i].length == 2
1 <= x, y, ai, bi <= 104
题解:
简单的数组扫描记录答案。
class Solution {
public:
int nearestValidPoint(int x, int y, vector<vector<int>>& points) {
int ans = -1, len = 0x3f3f3f3f;
int n = points.size();
for(int i = 0; i < n; i++)
{
if(x == points[i][0] || y == points[i][1])
{
int t = abs(x - points[i][0]) + abs(y - points[i][1]);
if(t < len)
{
len = t;
ans = i;
}
}
}
return ans;
}
};
第二题
LC 1780. 判断一个数字是否可以表示成三的幂的和
难度:中等
算法:位运算,枚举,DFS
题目描述:
给你一个整数 n ,如果你可以将 n 表示成若干个不同的三的幂之和,请你返回 true ,否则请返回 false 。
对于一个整数 y ,如果存在整数 x 满足 y == 3x ,我们称这个整数 y 是三的幂。
提示:
1 <= n <= 107
题解:
观察数据范围,则n最大用315表示足够,可以枚举3的0~15次幂的选择情况,共216种,判断出所有情况中是否存在一种加和为n。
时间复杂度:O(216)
class Solution {
public:
int num[18];
bool checkPowersOfThree(int n) {
for(int i = 0; i < 16; i++) num[i] = pow(3, i);
for(int i = 0; i < 1 << 16; i++)
{
int t = 0;
for(int j = 0; j < 16; j++)
{
if(i >> j & 1) t += num[j];
}
if(t == n) return true;
}
return false;
}
};
考虑n的三进制表示,如果n可以表示为条件所述,那么其三进制中一定都是0或者1,也即当且仅当三进制表示中只有0或1,才可以表示为条件所述。
依次得到n的三进制表示的每一位,判断即可。
时间复杂度:O(n / 3)
class Solution {
public:
bool checkPowersOfThree(int n) {
while(n)
{
if(n % 3 == 2) return false;
n /= 3;
}
return true;
}
};
第三题
LC 1781. 所有子字符串美丽值之和
难度:中等
算法:双指针,模拟
题目描述:
一个字符串的 美丽值 定义为:出现频率最高字符与出现频率最低字符的出现次数之差。
比方说,“abaacc” 的美丽值为 3 - 1 = 2 。
给你一个字符串 s ,请你返回它所有子字符串的 美丽值 之和。
提示:
1 <= s.length <= 500
s 只包含小写英文字母。
题解:
双指针i, j扫描子串头尾,j不断右移,对每个子串进行计算。动态统计出s[i~j]之间字符最高频率和最低频率,累计答案。
细节:在枚举子串时,由于要知道s[i~j]中每个字符出现次数,那么最好对于子串头i,让子串尾j从前向后移动,这样之前统计的次数后面可以利用到,省去每次在计算个数时还要重新扫描整个子串区间。
时间复杂度:O(n2 * 26)
class Solution {
public:
int beautySum(string s) {
int n = s.size(), ans = 0;
for(int i = 0; i < n; i++)
{
vector<int> cnt(26);
cnt[s[i] - 'a']++;
for(int j = i + 1; j < n; j++)
{
int mx = 0, mi = 510;
cnt[s[j] - 'a']++;
for(int i = 0; i < 26; i++)
{
if(cnt[i])
{
mx = max(cnt[i], mx);
mi = min(cnt[i], mi);
}
}
ans += (mx - mi);
}
}
return ans;
}
};
第四题
LC 1782. 统计点对的数目
难度:困难
算法:双指针,分类讨论,容斥原理
题目描述:
给你一个无向图,无向图由整数 n ,表示图中节点的数目,和 edges 组成,其中 edges[i] = [ui, vi] 表示 ui 和 vi 之间有一条无向边。同时给你一个代表查询的整数数组 queries 。
第 j 个查询的答案是满足如下条件的点对 (a, b) 的数目:
请你返回一个数组 answers ,其中 answers.length == queries.length 且 answers[j] 是第 j 个查询的答案。
请注意,图中可能会有 重复边 。
提示:
2 <= n <= 2 * 104
1 <= edges.length <= 105
1 <= ui, vi <= n
ui != vi
1 <= queries.length <= 20
0 <= queries[j] < edges.length
题解:
首先考虑给出点a, b,如何计算出题中定义的cnt。可以实现根据edges计算出与每个点相连的边的数量,同时对于两端点之间有多少边相连。
则:cnt = count(a) + count(b) - count(a ↔ b)
题目要求对与每个queries[i]求出满足上式cnt > queries[i]的点对数量。
分情况来看:设 sum = sum1 + sum2
对于sum1可以借助edges数组得到 ,考虑sum2如何得到?
定义sum3,sum4如下:
根据容斥原理知:sum2 = sum3 - sum4
问题转化为如何求出sum3和sum4?
对于sum4仍然可以类似sum1,借助edges数组求出。
而sum3可以对cnt数组排序,抽离模型为在一个有序数组中求出有多少的二元组(i, j)满足cnt[i] + cnt[j] > queries[i],经典双指针扫描问题。
最后逐个击破,得到sum。
时间复杂度:O(len + m * (n + len)) 其中len为edges.length
#define MP(x, y) make_pair(x, y)
#define x first
#define y second
class Solution {
public:
typedef pair<int, int> PII;
vector<int> countPairs(int n, vector<vector<int>>& edges, vector<int>& queries) {
vector<int> cnt(n + 1);
map<PII, int> mp;
for(auto& e : edges)
{
int a = e[0], b = e[1];
cnt[a]++;
cnt[b]++;
if(a > b) swap(a, b);
mp[{a, b}]++;
//mp[MP(a, b)]++;
}
vector<int> backup = cnt;
sort(backup.begin(), backup.end());
int m = queries.size();
vector<int> res(m);
for(int i = 0; i < m; i++)
{
int lim = queries[i], sum = 0;
for(auto& [a, b] : mp)
{
int u = a.x, v = a.y;
if(cnt[u] + cnt[v] - b > lim) sum++;
if(cnt[u] + cnt[v] > lim) sum--;
}
int l = 1, r = n;
while(l < r)
{
while(l < r && backup[l] + backup[r] <= lim) l++;
sum += (r - l);
r--;
}
res[i] = sum;
}
return res;
}
};
排名:243 / 3085(前7.88%)