第205场周赛
替换所有的问号
直接模拟即可,如果是’?’,记录前后字符。
1 | class Solution { |
2 | public String modifyString(String s) { |
3 | char[] str = s.toCharArray(); |
4 | int n = s.length(); |
5 | for(int i=0; i<n; i++){ |
6 | if(str[i] == '?'){ |
7 | int[] used = new int['z'-'a'+1]; |
8 | if(i-1>=0 && str[i-1]!='?'){ |
9 | used[str[i-1]-'a'] = 1; |
10 | } |
11 | if(i+1<n && str[i+1]!='?'){ |
12 | used[str[i+1]-'a'] = 1; |
13 | } |
14 | for(int j=0; j<'z'-'a'+1; j++){ |
15 | if(used[j] == 0){ |
16 | str[i] = (char)('a'+j); |
17 | break; |
18 | } |
19 | } |
20 | } |
21 | } |
22 | return new String(str); |
23 | } |
24 | } |
复杂度分析:
- 时间复杂度:$O(n)$。
- 空间复杂度:$O(n*26)$。
2.数的平方等于两数乘积的方法数
也是直接模拟,可以先做预处理,即求出某一数组的平方,用map存储,这里需要注意要使用long
,因为int
为溢出。
1 | class Solution { |
2 | public int numTriplets(int[] nums1, int[] nums2) { |
3 | return get(nums1, nums2) + get(nums2, nums1); |
4 | } |
5 | |
6 | public int get(int[] a, int[] b){ |
7 | int ans = 0; |
8 | Map<Long, Integer> map = new HashMap<Long, Integer>(); |
9 | for(int i=0; i<b.length; i++){ |
10 | long res = (long)b[i] * b[i]; |
11 | int count = map.getOrDefault(res, 0); |
12 | map.put(res, count+1); |
13 | } |
14 | for(int i=0; i<a.length; i++){ |
15 | for(int j=i+1; j<a.length; j++){ |
16 | long target = (long) a[i] * a[j]; |
17 | if(map.containsKey(target)){ |
18 | ans += map.get(target); |
19 | } |
20 | } |
21 | } |
22 | return ans; |
23 | } |
24 | } |
复杂度分析:
- 时间复杂度:$O(n^2)$。
- 空间复杂度:$O(n)$。
3.避免重复字母的最小删除成本
采用贪心策略,每次删除成本最小的,如果遇到一连串重复的字母,例如aaaa
,那么最后必定只剩下一个a
,那么只需留下成本最高的那么字母a
即可。
1 | class Solution { |
2 | public int minCost(String s, int[] cost) { |
3 | int ans = 0; |
4 | char[] str = s.toCharArray(); |
5 | for(int i=0; i<s.length(); i++){ |
6 | int sum = cost[i]; |
7 | int max = cost[i]; |
8 | char c = str[i]; |
9 | while(i+1<s.length() && str[i+1]==c){ |
10 | i++; |
11 | sum += cost[i]; |
12 | max = Math.max(max, cost[i]); |
13 | } |
14 | ans += sum-max; |
15 | } |
16 | return ans; |
17 | } |
18 | } |
复杂度分析:
- 时间复杂度:$O(n)$。
- 空间复杂度:$O(n)$。
4.保证图可完全遍历
对于Alice来说,如果图可完全遍历,那么类型1和类型3的边的数目必定为n-1(n为图中节点数)。对于Bob来说也一样,那么我们可以使用并查集来计算边数,如果不为n-1,那么就返回-1。之后再计算多余的边数,对于Alice来说,如果图可完全遍历,那么除去公共边(类型3的边),只需要n-1-commonEdge
的边数,对于Bob来说也是这样。那么总的来说对于Alice和Bob,如果两人都想要图可完全遍历,那么只需要2*(n-1-commonEdge)+commonEdge=2*(n-1)-commonEdge
即可,其余的边就是多余的边。其中commonEdge
的数目可以用并查集来确定。在并查集算法中,对于每一条边对应的两个顶点,我们检查这两个顶点是否已经在一个连通分量中,若是,则这条边为冗余边,可以直接去掉。这也是寻找最小生成树的过程。
1 | class Solution { |
2 | public int maxNumEdgesToRemove(int n, int[][] edges) { |
3 | DSU dsu = new DSU(n+1); |
4 | dsu.reset(); |
5 | int t1 = 0; |
6 | for(int[] e : edges){ |
7 | if(e[0]!=2 && dsu.find(e[1])!=dsu.find(e[2])){ |
8 | t1++; |
9 | dsu.merge(e[1], e[2]); |
10 | } |
11 | } |
12 | dsu.reset(); |
13 | int t2 = 0; |
14 | for(int[] e : edges){ |
15 | if(e[0]!=1 && dsu.find(e[1])!=dsu.find(e[2])){ |
16 | t2++; |
17 | dsu.merge(e[1], e[2]); |
18 | } |
19 | } |
20 | if(t1!=n-1 || t2!=n-1){ |
21 | return -1; |
22 | } |
23 | int cnt = 0; |
24 | dsu.reset(); |
25 | for(int[] e : edges){ |
26 | if(e[0]==3 && dsu.find(e[1])!=dsu.find(e[2])){ |
27 | cnt++; |
28 | dsu.merge(e[1], e[2]); |
29 | } |
30 | } |
31 | int retain = 2*(n-1)-cnt; |
32 | return edges.length-retain; |
33 | } |
34 | } |
35 | |
36 | public class DSU{ |
37 | int[] p; |
38 | int[] rank; |
39 | |
40 | public DSU(int n){ |
41 | p = new int[n]; |
42 | rank = new int[n]; |
43 | reset(); |
44 | } |
45 | |
46 | public void reset(){ |
47 | for(int i=0; i<p.length; i++){ |
48 | p[i] = i; |
49 | rank[i] = 0; |
50 | } |
51 | } |
52 | |
53 | public int find(int a){ |
54 | if(p[a] == p[p[a]]){ |
55 | return p[a]; |
56 | } |
57 | return p[a] = find(p[a]); |
58 | } |
59 | |
60 | public void merge(int a, int b){ |
61 | a = find(a); |
62 | b = find(b); |
63 | if(a == b){ |
64 | return; |
65 | } |
66 | if(rank[a] == rank[b]){ |
67 | rank[a]++; |
68 | } |
69 | if(rank[a] < rank[b]){ |
70 | int tmp = b; |
71 | b = a; |
72 | a = tmp; |
73 | } |
74 | p[b] = a; |
75 | } |
76 | } |
复杂度分析:
- 时间复杂度:$O(n)$。
- 空间复杂度:$O(n)$。