LeetCode Number of Good Pairs

1512. Number of Good Pairs

Given an array of integers nums.

A pair (i,j) is called good if nums[i] == nums[j] and i < j.

Return the number of good pairs.

Example 1:

Input: nums = [1,2,3,1,1,3]
Output: 4
Explanation: There are 4 good pairs (0,3), (0,4), (3,4), (2,5) 0-indexed.

Example 2:

Input: nums = [1,1,1,1]
Output: 6
Explanation: Each pair in the array are good.

Example 3:

Input: nums = [1,2,3]
Output: 0

Constraints:

  • 1 <= nums.length <= 100
  • 1 <= nums[i] <= 100

直接按题意做就行:

class Solution {
public:
	int numIdenticalPairs(vector<int>& nums) {
		int ans = 0, n = nums.size();
		for (int i = 0; i < n; ++i) {
			for (int j = i + 1; j < n; ++j) {
				if (nums[i] == nums[j])++ans;
			}
		}
		return ans;
	}
};

本代码提交AC,用时8MS。

LeetCode Path with Maximum Probability

1514. Path with Maximum Probability

You are given an undirected weighted graph of n nodes (0-indexed), represented by an edge list where edges[i] = [a, b] is an undirected edge connecting the nodes a and b with a probability of success of traversing that edge succProb[i].

Given two nodes start and end, find the path with the maximum probability of success to go from start to end and return its success probability.

If there is no path from start to endreturn 0. Your answer will be accepted if it differs from the correct answer by at most 1e-5.

Example 1:

Input: n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.2], start = 0, end = 2
Output: 0.25000
Explanation: There are two paths from start to end, one having a probability of success = 0.2 and the other has 0.5 * 0.5 = 0.25.

Example 2:

Input: n = 3, edges = [[0,1],[1,2],[0,2]], succProb = [0.5,0.5,0.3], start = 0, end = 2
Output: 0.30000

Example 3:

Input: n = 3, edges = [[0,1]], succProb = [0.5], start = 0, end = 2
Output: 0.00000
Explanation: There is no path between 0 and 2.

Constraints:

  • 2 <= n <= 10^4
  • 0 <= start, end < n
  • start != end
  • 0 <= a, b < n
  • a != b
  • 0 <= succProb.length == edges.length <= 2*10^4
  • 0 <= succProb[i] <= 1
  • There is at most one edge between every two nodes.

给定一个无向图,边表示概率,问从start到end的最大概率是多少。

借此题复习一下若干最短路径算法吧。

首先朴素的DFS,代码如下:

class Solution {
private:
	void DFS(const vector<vector<double>> &graph, vector<int> &visited, int start, int end, double curprob, double &maxprob) {
		
		if (start == end) {
			maxprob = max(maxprob, curprob);
			return;
		}

		int n = graph.size();
		for (int i = 0; i < n; ++i) {
			if (visited[i] == 0 && graph[start][i] >= 0) {
				visited[i] = 1;
				DFS(graph, visited, i, end, curprob*graph[start][i], maxprob);
				visited[i] = 0;
			}
		}

	}
public:
	double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
		vector<vector<double>> graph(n, vector<double>(n, -1.0));
		for (int i = 0; i < edges.size(); ++i) {
			graph[edges[i][0]][edges[i][1]] = succProb[i];
			graph[edges[i][1]][edges[i][0]] = succProb[i];
		}
		vector<int> visited(n, 0);
		visited[start] = 1;
		double maxprob = 0;
		DFS(graph, visited, start, end, 1, maxprob);
		return maxprob;
	}
};

本代码提交TLE。

其次, 朴素的迪杰斯特拉算法。迪杰斯特拉算法思路很简单,维护两个集合,一个集合S是已经找到最短路径的,另一个集合U是还未找到最短路径的。每次,从U选一个距离最小的节点u,这个节点已经是最短路径了,加入到S中。然后更新与u相连的其他节点的最短路径。如此循环往复。代码如下:

// 朴素迪杰斯特拉
class Solution {

public:
	double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
		vector<unordered_map<int, double>> graph(n, unordered_map<int, double>());
		for (int i = 0; i < edges.size(); ++i) {
			graph[edges[i][0]][edges[i][1]] = succProb[i];
			graph[edges[i][1]][edges[i][0]] = succProb[i];
		}

		vector<int> visited(n, 0);
		visited[start] = 1;

		vector<double> ans(n, 0);
		for (int i = 0; i < n; ++i) {
			ans[i] = graph[start][i];
		}

		while (true) {
			int maxid = -1;
			double maxprob = 0;
			for (int i = 0; i < n; ++i) {
				if (visited[i] == 0 && ans[i] > maxprob) {
					maxid = i;
					maxprob = ans[i];
				}
			}
			if (maxid == -1 || maxid == end)break; // 遇到end提前结束

			visited[maxid] = 1;


			for (unordered_map<int, double>::iterator it = graph[maxid].begin(); it != graph[maxid].end(); ++it) {
				int next = it->first;
				double prob = it->second;

				if (visited[next] == 0 && (maxprob*prob > ans[next])) {
					ans[next] = maxprob * prob;
				}

			}
		}

		return ans[end];
	}
};

很遗憾,上述代码在最后一个样例上TLE了。

优化版本的迪杰斯特拉算法。朴素迪杰斯特拉算法最耗时的是while循环内的两个for循环,第二个for循环是对找到的u节点的边进行循环,这个无法再优化,主要优化第一个for循环,即在U集合中找距离最短的u节点的过程。这里可以用优先队列来存储U中的所有节点。但是需要注意的是去重,比如题目中的第一个图,对于节点2,节点0弹出pq时,会访问节点2并把节点2加入到pq中;当节点1弹出pq时,又会访问节点2并把节点2加入到pq中,此时pq中出现了两个节点2,只不过prob不一样。所以pq弹出时可能会出现相同的节点,不过由于pq的性质,同样是2,先弹出来的肯定是概率最大的路径(最短路径),所以每次pq弹出时,更新visited数组,后续如果弹出的节点已经visited了,则不需要做第二个for循环了,直接continue。比如题目中的第一个图,0访问2时,压pq的是(id=2,prob=0.2);1访问2时,压pq的是(id=2,prob=0.25)。所以(id=2,prob=0.25)先于(id=2,prob=0.2)弹出,并设置visited[2]=1。当(id=2,prob=0.2)弹出时,visited[2]已经==1了,此时continue。


// 优化的迪杰斯特拉
struct P {
	int id_;
	double prob_;
	P(int id, double prob) :id_(id), prob_(prob) {};

	// priority_queue默认是最大堆,即小于就是小于
	bool operator<(const P& p) const {
		return this->prob_ < p.prob_;
	}
};

class Solution {

public:
	double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
		vector<unordered_map<int, double>> graph(n, unordered_map<int, double>());
		for (int i = 0; i < edges.size(); ++i) {
			graph[edges[i][0]][edges[i][1]] = succProb[i];
			graph[edges[i][1]][edges[i][0]] = succProb[i];
		}

		vector<int> visited(n, 0);

		vector<double> ans(n, 0);
		ans[start] = 1;

		priority_queue<P> pq;
		pq.push(P(start, 1));

		while (!pq.empty()) {
			P cur = pq.top();
			pq.pop();
			
			if (cur.id_ == end)break; // 提前结束

			if (visited[cur.id_] == 1)continue;
			visited[cur.id_] = 1;

			for (unordered_map<int, double>::iterator it = graph[cur.id_].begin(); it != graph[cur.id_].end(); ++it) {
				int next = it->first;
				double prob = it->second;
				if (cur.prob_*prob > ans[next]) {
					ans[next] = cur.prob_*prob;
					pq.push(P(next, ans[next]));
				}
			}

		}
		return ans[end];
	}
};

本代码提交AC,用时428MS。

SPFA算法。SPFA算法的思想可以参考之前的一道题: http://code.bitjoy.net/2014/12/28/hihocoder-1093/

简单来说,SPFA就是带剪枝的BFS。它和迪杰斯特拉算法非常像,迪杰斯特拉算法是每次找U中距离最小的一个节点u,此时u的最短距离已经确定了,后续不会再更新了。因为迪杰斯特拉算法每次加入S的节点都是最优的,所以必须要线性(朴素迪杰斯特拉算法)或者用优先队列的方法从U中找到最小距离节点u。而SPFA算法就更简单一点,该算法不要求每次找到最优的节点u,因为SPFA是渐进逼近最优解的过程,它不需要线性查找,也不需要优先队列,它只需要一个简单的队列即可,每次把可以更新的节点u加入到队列中,然后BFS更新与u相连的其他节点。之前更新过的节点,后续还有可能更新。代码如下:

// SPFA
class Solution {

public:
	double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
		vector<unordered_map<int, double>> graph(n, unordered_map<int, double>());
		for (int i = 0; i < edges.size(); ++i) {
			graph[edges[i][0]][edges[i][1]] = succProb[i];
			graph[edges[i][1]][edges[i][0]] = succProb[i];
		}

		vector<int> visited(n, 0);
		visited[start] = 1;

		vector<double> ans(n, 0);
		ans[start] = 1;

		queue<int> q;
		q.push(start);
		while (!q.empty()) {
			int cur = q.front();
			q.pop();
			visited[cur] = 0;

			for (unordered_map<int, double>::iterator it = graph[cur].begin(); it != graph[cur].end(); ++it) {
				int next = it->first;
				double prob = it->second;
				if (ans[cur] * prob > ans[next]) {
					ans[next] = ans[cur] * prob;
					if (visited[next] == 0) {
						visited[next] = 1;
						q.push(next);
					}
				}
			}
		}

		return ans[end];
	}
};

本代码提交AC,用时392MS。

剑指 Offer 06. 从尾到头打印链表

剑指 Offer 06. 从尾到头打印链表

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

输入:head = [1,3,2]
输出:[2,3,1]
 

限制:

0 <= 链表长度 <= 10000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


给定一个单链表,从尾到头打印链表的值。先顺序遍历链表,将结果存到一个数组中,然后逆序数组。代码如下:

class Solution {
public:
    vector<int> reversePrint(ListNode* head) {
        if(head == NULL) return {};
        vector<int> ans;
        while(head != NULL) {
            ans.push_back(head->val);
            head = head->next;
        }
        int i = 0, j = ans.size() - 1;
        while(i < j) {
            swap(ans[i++], ans[j--]);
        }
        return ans;
    }
};

本代码提交AC,用时4MS。

剑指 Offer 05. 替换空格

剑指 Offer 05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成”%20″。

示例 1:

输入:s = “We are happy.”
输出:”We%20are%20happy.”
 

限制:

0 <= s 的长度 <= 10000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


直接暴力替换即可,代码如下:

class Solution {
public:
    string replaceSpace(string s) {
        string ans = "";
        for(int i = 0; i < s.size(); ++i) {
            if(s[i] == ' ') ans += "%20";
            else ans += s[i];
        }
        return ans;
    }
};

本代码提交AC,用时0MS。

剑指 Offer 04. 二维数组中的查找

剑指 Offer 04. 二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。

给定 target = 20,返回 false。

限制:

0 <= n <= 1000

0 <= m <= 1000

注意:本题与主站 240 题相同:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


给定一个矩阵,每行从左到右升序排列,每列从上到下升序排列,给定一个数,问这个数在不在矩阵中。

从矩阵右上角开始查找,代码如下:

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        if(matrix.empty() || matrix[0].empty()) return false;
        int n = matrix.size(), m = matrix[0].size();
        int i = 0, j = m - 1;
        while(i < n && j >= 0) {
            if(matrix[i][j] == target) return true;
            if(target > matrix[i][j]) ++i;
            else --j;
        }
        return false;
    }
};

本代码提交AC,用时64MS。

剑指 Offer 03. 数组中重复的数字

剑指 Offer 03. 数组中重复的数字

找出数组中重复的数字。

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
 

限制:

2 <= n <= 100000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


查找数组中的重复元素。直接用数组hash,代码如下:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int n = nums.size();
        vector<int> hash(n, 0);
        for(int i = 0; i < n; ++i) {
            ++hash[nums[i]];
            if(hash[nums[i]] > 1) {
                return nums[i];
            }
        }
        return 0;
    }
};

本代码提交AC,用时84MS。

二刷。这题有陷阱,请注意和Find the Duplicate Number不一样!Leetcode英文题中,数组长度是n+1,数的范围是[1,n],也就是说根据鸽巢原理,数组必定有一个重复元素。而本题中,数组长度是n,数的范围是[0,n-1],也就是说数组可以没有重复元素,只不过这题告诉你至少有一个重复元素,要找出来。

这题不能用英文题中的二分的方法,比如n=6数组[0,0,2,3,4,5],l=0,r=5,m=2,统计[0,2]区间的数的个数=3,满足3<=m+1,因为即使是无重复数组[0,1,2,3,4,5],也是3<=m+1,所以无法区分左边区间是否有重复。

看题解,O(1)的方法是。如果数组没有重复元素,则排序之后nums[i]=i,所以我们模拟排序的过程,遍历数组,如果nums[i]!=i,则把nums[i]放到它排序后应该在的位置,即放到nums[nums[i]]的位置。如果nums[i]和它要放的位置nums[nums[i]]相等,说明这个元素重复出现了。完整代码如下:

class Solution {
public:
    int findRepeatNumber(vector<int>& nums) {
        int n = nums.size();
        for(int i = 0; i < n; ++i) {
            while(nums[i] != i) {
                if(nums[i] == nums[nums[i]]) return nums[i];
                swap(nums[i],nums[nums[i]]);
            }
        }
        return 0;
    }
};

本代码提交AC,用时92MS。

LeetCode Last Moment Before All Ants Fall Out of a Plank

5453. Last Moment Before All Ants Fall Out of a Plank

We have a wooden plank of the length n units. Some ants are walking on the plank, each ant moves with speed 1 unit per second. Some of the ants move to the left, the other move to the right.

When two ants moving in two different directions meet at some point, they change their directions and continue moving again. Assume changing directions doesn’t take any additional time.

When an ant reaches one end of the plank at a time t, it falls out of the plank imediately.

Given an integer n and two integer arrays left and right, the positions of the ants moving to the left and the right. Return the moment when the last ant(s) fall out of the plank.

Example 1:

Input: n = 4, left = [4,3], right = [0,1]
Output: 4
Explanation: In the image above:
-The ant at index 0 is named A and going to the right.
-The ant at index 1 is named B and going to the right.
-The ant at index 3 is named C and going to the left.
-The ant at index 4 is named D and going to the left.
Note that the last moment when an ant was on the plank is t = 4 second, after that it falls imediately out of the plank. (i.e. We can say that at t = 4.0000000001, there is no ants on the plank).

Example 2:

Input: n = 7, left = [], right = [0,1,2,3,4,5,6,7]
Output: 7
Explanation: All ants are going to the right, the ant at index 0 needs 7 seconds to fall.

Example 3:

Input: n = 7, left = [0,1,2,3,4,5,6,7], right = []
Output: 7
Explanation: All ants are going to the left, the ant at index 7 needs 7 seconds to fall.

Example 4:

Input: n = 9, left = [5], right = [4]
Output: 5
Explanation: At t = 1 second, both ants will be at the same intial position but with different direction.

Example 5:

Input: n = 6, left = [6], right = [0]
Output: 6

Constraints:

  • 1 <= n <= 10^4
  • 0 <= left.length <= n + 1
  • 0 <= left[i] <= n
  • 0 <= right.length <= n + 1
  • 0 <= right[i] <= n
  • 1 <= left.length + right.length <= n + 1
  • All values of left and right are unique, and each value can appear only in one of the two arrays.

很有意思的一个题。一块长木板上,放了若干只蚂蚁,有的蚂蚁向左走,有的蚂蚁向右走。当两个蚂蚁碰头时,会各自掉头走。问所有蚂蚁走出木板的时间。

有点像智力题,分析了半天,发现挺简单的。想象一下,如果蚂蚁A向右走,蚂蚁B向左走,它们在坐标0点碰头了,则A掉头向左走,B掉头向右走。对于A和B来说,虽然它们的方向变了,但是对于整个木板来说,并没有差别,依然是有一只蚂蚁从0点向右走,有一只蚂蚁从0点向左走。相当于A和B交换了身份,各自帮对方走了剩下的流程。但是从宏观分析看,A和B不管掉不掉头,效果是一样的。

综上所述,完全不用考虑碰头掉头的问题,直接对向左走和向右走的蚂蚁单独分析。对于向左走的蚂蚁,最右边的蚂蚁要走最长的时间。对于向右走的蚂蚁,最左边的蚂蚁要走最长的时间。总时间就是这两种情况的最大值。代码如下:

class Solution {
public:
	int getLastMoment(int n, vector<int>& left, vector<int>& right) {
		sort(left.begin(), left.end());
		sort(right.begin(), right.end());
		int ans = 0;
		if (!left.empty())ans = max(ans, left.back());
		if (!right.empty())ans = max(ans, n - right.front());
		return ans;
	}
};

本代码提交AC。

LeetCode Can Make Arithmetic Progression From Sequence

5452. Can Make Arithmetic Progression From Sequence

Given an array of numbers arr. A sequence of numbers is called an arithmetic progression if the difference between any two consecutive elements is the same.

Return true if the array can be rearranged to form an arithmetic progression, otherwise, return false.

Example 1:

Input: arr = [3,5,1]
Output: true
Explanation: We can reorder the elements as [1,3,5] or [5,3,1] with differences 2 and -2 respectively, between each consecutive elements.

Example 2:

Input: arr = [1,2,4]
Output: false
Explanation: There is no way to reorder the elements to obtain an arithmetic progression.

Constraints:

  • 2 <= arr.length <= 1000
  • -10^6 <= arr[i] <= 10^6

给一个数组,问能否通过排列组合让这个数组变成等差数列。

直接排序,看看是否是等差数列即可:

class Solution {
public:
	bool canMakeArithmeticProgression(vector<int>& arr) {
		sort(arr.begin(), arr.end());
		int diff = arr[1] - arr[0];
		for (int i = 2; i < arr.size(); ++i) {
			if (arr[i] - arr[i - 1] != diff)return false;
		}
		return true;
	}
};

本代码提交AC。

LeetCode Avoid Flood in The City

1488. Avoid Flood in The City

Your country has an infinite number of lakes. Initially, all the lakes are empty, but when it rains over the nth lake, the nth lake becomes full of water. If it rains over a lake which is full of water, there will be a flood. Your goal is to avoid the flood in any lake.

Given an integer array rains where:

  • rains[i] > 0 means there will be rains over the rains[i] lake.
  • rains[i] == 0 means there are no rains this day and you can choose one lake this day and dry it.

Return an array ans where:

  • ans.length == rains.length
  • ans[i] == -1 if rains[i] > 0.
  • ans[i] is the lake you choose to dry in the ith day if rains[i] == 0.

If there are multiple valid answers return any of them. If it is impossible to avoid flood return an empty array.

Notice that if you chose to dry a full lake, it becomes empty, but if you chose to dry an empty lake, nothing changes. (see example 4)

Example 1:

Input: rains = [1,2,3,4]
Output: [-1,-1,-1,-1]
Explanation: After the first day full lakes are [1]
After the second day full lakes are [1,2]
After the third day full lakes are [1,2,3]
After the fourth day full lakes are [1,2,3,4]
There's no day to dry any lake and there is no flood in any lake.

Example 2:

Input: rains = [1,2,0,0,2,1]
Output: [-1,-1,2,1,-1,-1]
Explanation: After the first day full lakes are [1]
After the second day full lakes are [1,2]
After the third day, we dry lake 2. Full lakes are [1]
After the fourth day, we dry lake 1. There is no full lakes.
After the fifth day, full lakes are [2].
After the sixth day, full lakes are [1,2].
It is easy that this scenario is flood-free. [-1,-1,1,2,-1,-1] is another acceptable scenario.

Example 3:

Input: rains = [1,2,0,1,2]
Output: []
Explanation: After the second day, full lakes are  [1,2]. We have to dry one lake in the third day.
After that, it will rain over lakes [1,2]. It's easy to prove that no matter which lake you choose to dry in the 3rd day, the other one will flood.

Example 4:

Input: rains = [69,0,0,0,69]
Output: [-1,69,1,1,-1]
Explanation: Any solution on one of the forms [-1,69,x,y,-1], [-1,x,69,y,-1] or [-1,x,y,69,-1] is acceptable where 1 <= x,y <= 10^9

Example 5:

Input: rains = [10,20,20]
Output: []
Explanation: It will rain over lake 20 two consecutive days. There is no chance to dry any lake.

Constraints:

  • 1 <= rains.length <= 10^5
  • 0 <= rains[i] <= 10^9

给定一个数组rains,rains[i]表示第i天在第rains[i]的湖上下雨,湖被灌满,比如第四个例子,rains[0]=69表示第0天在第69号湖上下雨,第69号湖被灌满。如果rains[i]=0表示第i天不下雨,可以抽干某个湖。问每个不下雨的天,抽干哪个湖,能让任意一个湖都不至于出现水灾(被灌满之后又被下雨)。

很有意思的一个题,暴力方法是,每个不下雨的天,对于之前被灌满的湖,抽干未来第一个要下雨的湖。比如对于rains=[1,2,3,0,2,0,0],当遇到第一个0时,我们选择抽干2号湖,因为下一天马上要在2号湖上下雨,如果不抽干2号湖,则会发生水灾,所以要抽干未来第一个要出现水灾的湖。但是这种时间复杂度是O(n^2)。

参考讨论区,更好的做法是,先记录所有不下雨的天,然后对于每一个下雨天,如果要下雨的湖之前没被灌满,则不用管;如果之前被灌满了,则需要在之前被灌满那天之后找一个不下雨的天,把这个湖抽干。所以维护一个堆heap/set,记录之前不下雨的天,后续直接用lower_bound查找符合的天。

完整代码如下:

class Solution {
public:
	vector<int> avoidFlood(vector<int>& rains) {
		int n = rains.size();

		vector<int> ans;
		unordered_map<int, int> full_lakes;
		set<int> dry_days;

		for (int i = 0; i < n; ++i) {
			if (rains[i] == 0) {
				dry_days.insert(i);
				ans.push_back(1); // 先随便填一个数,后续会覆盖,填入真正要抽干的湖编号
			}
			else {
				if (full_lakes.find(rains[i]) != full_lakes.end()) { // 这个湖之前就满了,需要抽干
					int last_day = full_lakes[rains[i]];
					set<int>::iterator it = dry_days.lower_bound(last_day); //从之前满的那天往后选不下雨的一天抽干
					if (it == dry_days.end()) {
						return {}; // 失败
					}
					ans[*it] = rains[i];
					dry_days.erase(it);
				}
				full_lakes[rains[i]] = i; // 填入新的下雨日期
				ans.push_back(-1);
			}
		}

		return ans;
	}
};

本代码提交AC,用时1040MS。

LeetCode Making File Names Unique

1487. Making File Names Unique

Given an array of strings names of size n. You will create n folders in your file system such that, at the ith minute, you will create a folder with the name names[i].

Since two files cannot have the same name, if you enter a folder name which is previously used, the system will have a suffix addition to its name in the form of (k), where, k is the smallest positive integer such that the obtained name remains unique.

Return an array of strings of length n where ans[i] is the actual name the system will assign to the ith folder when you create it.

Example 1:

Input: names = ["pes","fifa","gta","pes(2019)"]
Output: ["pes","fifa","gta","pes(2019)"]
Explanation: Let's see how the file system creates folder names:
"pes" --> not assigned before, remains "pes"
"fifa" --> not assigned before, remains "fifa"
"gta" --> not assigned before, remains "gta"
"pes(2019)" --> not assigned before, remains "pes(2019)"

Example 2:

Input: names = ["gta","gta(1)","gta","avalon"]
Output: ["gta","gta(1)","gta(2)","avalon"]
Explanation: Let's see how the file system creates folder names:
"gta" --> not assigned before, remains "gta"
"gta(1)" --> not assigned before, remains "gta(1)"
"gta" --> the name is reserved, system adds (k), since "gta(1)" is also reserved, systems put k = 2. it becomes "gta(2)"
"avalon" --> not assigned before, remains "avalon"

Example 3:

Input: names = ["onepiece","onepiece(1)","onepiece(2)","onepiece(3)","onepiece"]
Output: ["onepiece","onepiece(1)","onepiece(2)","onepiece(3)","onepiece(4)"]
Explanation: When the last folder is created, the smallest positive valid k is 4, and it becomes "onepiece(4)".

Example 4:

Input: names = ["wano","wano","wano","wano"]
Output: ["wano","wano(1)","wano(2)","wano(3)"]
Explanation: Just increase the value of k each time you create folder "wano".

Example 5:

Input: names = ["kaido","kaido(1)","kaido","kaido(1)"]
Output: ["kaido","kaido(1)","kaido(2)","kaido(1)(1)"]
Explanation: Please note that system adds the suffix (k) to current name even it contained the same suffix before.

Constraints:

  • 1 <= names.length <= 5 * 10^4
  • 1 <= names[i].length <= 20
  • names[i] consists of lower case English letters, digits and/or round brackets.

给定一个字符串数组,表示每次创建的文件名,如果新的文件名和之前的文件名重名,则需要在后面加上最小的(k)重命名。问最终创建的文件名数组是怎样的。

使用hash表记录每个文件名前缀出现的次数,如果之前没出现,则直接合法;如果之前出现了,则合法的(k)至少是之前出现次数之后的k,枚举找到最小的k。代码如下:

class Solution {
public:
	vector<string> getFolderNames(vector<string>& names) {
		vector<string> ans;
		unordered_map<string, int> show_times;
		for (int i = 0; i < names.size(); ++i) {
			if (show_times.find(names[i]) == show_times.end()) {
				ans.push_back(names[i]);
				show_times[names[i]] = 1;
			}
			else {
				int k = show_times[names[i]];
				string next_name = names[i] + "(" + to_string(k) + ")";
				while (show_times.find(next_name) != show_times.end()) {
					show_times[names[i]] = ++k;
					next_name = names[i] + "(" + to_string(k) + ")";
				}
				ans.push_back(next_name);
				show_times[next_name] = 1;
			}
		}
		return ans;
	}
};

本代码提交AC,用时424MS。