力扣第 47 场双周赛题解

力扣第 47 场双周赛题解

    • 找到最近的有相同X或Y坐标的点
    • 判断一个数字是否可以表示成三的幂的和
    • 所有子字符串美丽值之和
    • 统计点对的数目
      • 题解
      • 具体代码:

第一次打力扣的比赛,有点不习惯。最后一题一开始进入误区了,而且调试改bug改了将近1个小时,改完后才发现真正解法,结果已经只剩5分钟,然后写完时比赛已经结束了5分钟了。本来代码习惯就一般,再加上急了所以代码写的pang乱。
前三道题确实简单,所以就不写题解了。

找到最近的有相同X或Y坐标的点

给你两个整数 xy ,表示你在一个笛卡尔坐标系下的 (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 n = points.size();
        int ans = -1;
        for(int i = 0;i < n;i ++){
            if(points[i][0] == x || points[i][1] == y){
                if(ans == -1)
                	ans = i;
                else{
                    if((points[i][0] - x) * (points[i][0] - x) + (points[i][1] - y) * (points[i][1] - y) < (points[ans][0] - x) * (points[ans][0] - x) + (points[ans][1] - y) * (points[ans][1] - y))
                        ans = i;
                }
            }
        }
        return ans;
    }
};

判断一个数字是否可以表示成三的幂的和

给你一个整数 n ,如果你可以将 n 表示成若干个不同的三的幂之和,请你返回 true ,否则请返回 false

对于一个整数 y ,如果存在整数 x 满足 y == 3x ,我们称这个整数 y 是三的幂。

class Solution {
public:
    bool checkPowersOfThree(int n) {
        int ma = 1;
    while(ma<=n)ma*=3;
    ma/=3;
    while(n>0){
        if(n>=ma)
            n-=ma;
        if(n>=ma) return false;
        ma/=3;
    }
    return true;
    }
};

所有子字符串美丽值之和

一个字符串的 美丽值 定义为:出现频率最高字符与出现频率最低字符的出现次数之差。

比方说,abaacc 的美丽值为 3 - 1 = 2
给你一个字符串 s ,请你返回它所有子字符串的 美丽值 之和。

1 <= s.length <= 500
s 只包含小写英文字母。

class Solution {
public:
    int beautySum(string s) {
        int len = s.length();
	    int num[505][30] = {0};
	    for(int i = 1;i <= len;i ++){
	        for(int j = 0;j < 26;j ++)
	            num[i][j] = num[i-1][j];
	        num[i][s[i-1] - 'a'] ++;
	    }
	    int ans = 0;
	    for(int i = 1;i < len;i ++){
	        for(int j= i + 1;j <= len;j ++){
	            int ma = 0,mi = 0x3f3f3f3f;
	            for(int k = 0;k < 26;k ++){
	                if(num[j][k] == num[i-1][k])
	                	continue;
	                mi = min(mi,num[j][k] - num[i-1][k]);
	                ma = max(ma,num[j][k] - num[i-1][k]);
	            }
	            ans += ma - mi;
	        }
	    }
	    return ans;
    }
};

统计点对的数目

给你一个无向图,无向图由整数 n ,表示图中节点的数目,和 edges 组成,其中 edges[i] = [ui, vi]表示 uivi之间有一条无向边。同时给你一个代表查询的整数数组 queries

j 个查询的答案是满足如下条件的点对 (a, b) 的数目:

  • a < b
  • cnt 是与 a 或者 b 相连的边的数目,且 cnt 严格大于 queries[j] 。

请你返回一个数组 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):

  • 与点a和点b这两个点中至少一个点相连接的边的数量大于querises[i]

也就是说,假设有n个点,那么我们要得出(1,2),(1,3)……(1,n)(2,3),(2,4)……(n-1,n)中有多少对满足上面的那个条件。
本题的难点在于当要判断(a,b)时,ab之间本身可能存在连线,但是我们不可能真的用 O(n^2) 的办法进行暴力计算。

根据题目给的数据范围,我们可以考虑从进行入手思考办法。

首先我们可以通过边,计算出每个点的度将其存入数组d中,使得d[i]则为点i的度。同时,我们为了得到一个根据度进行排序的数组,我们可以再创建一个数组来专门进行排序,得到从大到小的度。(代码中因为一开始用的不是这个方法,导致我写了个结构体排,后来换方法的时候也懒得改了 ,正好也方便我后面用map来进行标记)
我们这时候根据排序得到的度,可以在 O(n) 的时间复杂度内得到所有度相加大于querises[i]的点对数。具体实现方法如下,应该比较好理解:

int sum = 0;
int last = n;
for(int j = 1;j <= n;j ++){
    while(sort_d[j] + sort_d[last] <= z && last > j)
    	last --;
    if(last <= j)
    	break;
    sum += last - j;
}

此时得出的值当然不是最后的答案,根据分析我们可以发现,我们这时算出的答案中还存在一直情况,即d[a]+d[b]>z但是ab之间可能存在边,单纯的将这种情况的度相加,会导致ab之间相连的边计算的两次,而当d[a]+d[b]-num[a][b]<=z时, (a,b)应该是不能满足条件的 ,所有我们应该将这种情况去除。
我们应该遍历边,找出所有的d[a] + d[b] > z 并且d[a] + d[b] - num[a][b] <= z的点对数,用上一步得出的总数减去这部分即为最后的答案。(注意因为可能会出现重边,所以相同的点对要标记,不要多算了)
这里的num即是点对(a,b)之间的边的数量,当然不能用数组实现,应该采用map进行映射。

具体代码:

class Solution {
public:

struct nod{
    int num;
    int i;
    bool operator <(const nod& b)const{
        if(num!=b.num)
        return num>b.num;
        else return i>b.i;
    }
    bool operator >(const nod &b)const{
        if(num!=b.num)
        return num<b.num;
        else return i<b.i;
    }
    
};
vector<int> countPairs(int n, vector<vector<int>>& edges, vector<int>& queries) {
    nod ak[20005]={0};
    int dd[20005]={0};
    int m = edges.size();
    int q = queries.size();
    vector<int> ans;
    map< nod ,int > ma;
           
    for(int i = 1;i <= n;i ++)
        ak[i].i = i;
        
    for(int i = 0;i < m;i ++){
        ak[edges[i][0]].num ++;
        ak[edges[i][1]].num ++;
        dd[edges[i][0]] ++;
        dd[edges[i][1]] ++;
        nod temp;
        temp.i = edges[i][0];
        temp.num = edges[i][1];
        if(temp.i > temp.num)
        	swap(temp.i,temp.num);
        ma[temp] = ma[temp] + 1;
    }
    
    sort(ak+1,ak+n+1);
    
    for(int i=0;i<q;i++){
        int z = queries[i];
        int tans = 0;
        map< nod ,bool > sc;
        for(int j = 0;j < m;j ++){
            if(dd[edges[j][0]] + dd[edges[j][1]] > z){
                nod temp;
                temp.i = edges[j][0];
                temp.num = edges[j][1];
                if(temp.i > temp.num)
                	swap(temp.i,temp.num);
                if(dd[edges[j][0]]+dd[edges[j][1]]-ma[temp]<=z
                	&&!sc[temp]){
                    tans--;
                    sc[temp] = true;
                }
            }
        }
        int last = n;
        for(int j = 1;j <= n;j ++){
            while(ak[j].num + ak[last].num <= z
            	&&last>j)
            	last--;
            if(last <= j)
            	break;
            tans += last - j;
        }
        ans.push_back(tans);
    }
    return ans;
}
};

你可能感兴趣的:(数据结构,算法,leetcode)