Monthly Archives: March 2017

LeetCode Longest Word in Dictionary through Deleting

LeetCode Longest Word in Dictionary through Deleting Given a string and a string dictionary, find the longest string in the dictionary that can be formed by deleting some characters of the given string. If there are more than one possible results, return the longest word with the smallest lexicographical order. If there is no possible result, return the empty string. Example 1:

Input:
s = "abpcplea", d = ["ale","apple","monkey","plea"]
Output:
"apple"
Example 2:
Input:
s = "abpcplea", d = ["a","b","c"]
Output:
"a"
Note:
  1. All the strings in the input will only contain lower-case letters.
  2. The size of the dictionary won’t exceed 1,000.
  3. The length of all the strings in the input won’t exceed 1,000.

给定一个字典d和字符串s,问d中哪些字符串可以通过s删除若干个字符得到,要求输出满足条件的最长字符串,如果有多个,则输出字典序最小的。 本题有两个考点,一是判断t是否能通过删除s中的若干个字符得到。使用两个指向s,t的指针i,j,j不断后移找t[i],找到之后i,j都后移。最终如果找到t中所有字符,则成功,否则失败。 另一个考点是,找出所有满足条件的字符串之后,怎样找到长度最长且字典序最小的字符串。这可以通过自定义string的比较函数,然后sort得到。具体看代码: [cpp] bool comp(const string& s1, const string& s2) { return s1.size() > s2.size() || (s1.size() == s2.size() && s1 < s2); } class Solution { private: bool convert(const string& src, const string& dest){ int m = src.size(), n = dest.size(); if (m < n)return false; int i = 0, j = 0; while (i < m) { while (i < m && src[i] != dest[j])++i; if (i >= m)break; ++i; ++j; if (j == n)break; } return j == n; } public: string findLongestWord(string s, vector<string>& d) { vector<string> cand; for (int i = 0; i < d.size(); ++i) { if (convert(s, d[i]))cand.push_back(d[i]); } sort(cand.begin(), cand.end(), comp); if (cand.size() > 0)return cand[0]; else return ""; } }; [/cpp] 本代码提交AC,用时135MS。]]>

LeetCode Game of Life

289. Game of Life 289. Game of Life

According to the Wikipedia’s article: “The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970.”

Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article):

  1. Any live cell with fewer than two live neighbors dies, as if caused by under-population.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by over-population..
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

Write a function to compute the next state (after one update) of the board given its current state. The next state is created by applying the above rules simultaneously to every cell in the current state, where births and deaths occur simultaneously.

Example:

Input: 
[
  [0,1,0],
  [0,0,1],
  [1,1,1],
  [0,0,0]
]
Output: 
[
  [0,0,0],
  [1,0,1],
  [0,1,1],
  [0,1,0]
]

Follow up:

  1. Could you solve it in-place? Remember that the board needs to be updated at the same time: You cannot update some cells first and then use their updated values to update other cells.
  2. In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. How would you address these problems? 289. Game of Life

本题有关康威生命游戏,第一次听说。。。 给一个二维矩阵,0表示死亡,1表示活着。每个细胞根据周围8个细胞的状态,有如下跳转规则:

  1. 活着的细胞周围有少于2个活着的细胞,则该细胞死亡
  2. 活着的细胞周围有2个或3个活着的细胞,则该细胞继续活着
  3. 活着的细胞周围有多于3个活着的细胞,则该细胞死亡
  4. 死亡的细胞周围有3个活着的细胞,则该细胞复活

最简单的方法是复制一个矩阵,遍历原矩阵然后更新新矩阵。但本题的难点是,要在in-place的情况下更新原矩阵。如果还是像之前那样更新的话,当前细胞的状态更新之后,会影响到后续细胞的判断,不可行。 参考网上题解,使用编码方法。 因为输入是以int来存储细胞状态的,int可以存储的状态数就很多了,不仅仅局限于0和1。 假设细胞状态重新编码如下:

  • 0:0→0,原来死亡,现在还死亡
  • 1:1→1,原来活着,现在还活着
  • 2:1→0,原来活着,现在死亡
  • 3:0→1,原来死亡,现在活着

则还是像之前一样,遍历细胞的周围8个点,根据以上编码可以知道周围细胞更新前的状态,比如某个邻居的状态是2,则知道该邻居更新前是1活着的。 所有细胞都更新完之后,需要复原成0,1编码,则状态为0和2表示新状态是死亡0,状态是1和3表示新状态是活着1。这可以用新状态对2取余数得到。 代码如下:

class Solution {
public:
    void gameOfLife(vector<vector<int> >& board)
    {
        int m = board.size(), n = 0;
        if (m != 0)
            n = board[0].size();
        if (m == 0 || n == 0)
            return;
        vector<vector<int> > dirs = { { -1, -1 }, { -1, 0 }, { -1, 1 }, { 0, -1 }, { 0, 1 }, { 1, -1 }, { 1, 0 }, { 1, 1 } };
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                int lives = 0;
                for (int k = 0; k < dirs.size(); ++k) {
                    int x = i + dirs[k][0], y = j + dirs[k][1];
                    if (x >= 0 && x < m && y >= 0 && y < n && (board[x][y] == 1 || board[x][y] == 2))
                        ++lives;
                }
                if (board[i][j] == 1 || board[i][j] == 2) {
                    if (lives < 2 || lives > 3)
                        board[i][j] = 2;
                    else
                        board[i][j] = 1;
                }
                else if (lives == 3)
                    board[i][j] = 3;
            }
        }
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n; ++j) {
                board[i][j] %= 2;
            }
        }
    }
};

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

二刷。 很简单,也不用定什么规则,直接按位编码,第0位是原来的状态,0为死1为生,第1位是更新后的状态,依然是0为死1为生,所有状态更新完之后,右移一位即可。代码如下:

class Solution {
public:
	void gameOfLife(vector<vector<int>>& board) {
		int m = board.size(), n = board[0].size();

		for (int i = 0; i < m; ++i) {
			for (int j = 0; j < n; ++j) {

				int lives = 0;
				int u = max(0, i - 1), v = max(0, j - 1);
				int x = min(m, i - 1 + 3), y = min(n, j - 1 + 3);
				for (int a = u; a < x; ++a) {
					for (int b = v; b < y; ++b) {
						if (a == i && b == j)continue;
						if ((board[a][b] & 1) == 1)++lives;
					}
				}

				int curlive = (board[i][j] & 1);
				if (curlive == 1 && lives < 2) {
					board[i][j] = (board[i][j] | (0 << 1));
				}
				else if (curlive == 1 && (lives == 2 || lives == 3)) {
					board[i][j] = (board[i][j] | (1 << 1));
				}
				else if (curlive == 1 && (lives > 3)) {
					board[i][j] = (board[i][j] | (0 << 1));
				}
				else if (curlive == 0 && lives == 3) {
					board[i][j] = (board[i][j] | (1 << 1));
				}

			}
		}

		for (int i = 0; i < m; ++i) {
			for (int j = 0; j < n; ++j) {
				board[i][j] >>= 1;
			}
		}

	}
};

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

LeetCode Ones and Zeroes

LeetCode Ones and Zeroes In the computer world, use restricted resource you have to generate maximum benefit is what we always want to pursue. For now, suppose you are a dominator of m 0s and n 1s respectively. On the other hand, there is an array with strings consisting of only 0s and 1s. Now your task is to find the maximum number of strings that you can form with given m 0s and n 1s. Each 0 and 1 can be used at most once. Note:

  1. The given numbers of 0s and 1s will both not exceed 100
  2. The size of given string array won’t exceed 600.
Example 1:
Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3
Output: 4
Explanation: This are totally 4 strings can be formed by the using of 5 0s and 3 1s, which are “10,”0001”,”1”,”0”
Example 2:
Input: Array = {"10", "0", "1"}, m = 1, n = 1
Output: 2
Explanation: You could form "10", but then you'd have nothing left. Better form "0" and "1".

给定一个0,1字符串数组,问最多能从中选出多少个字符串,使得所有字符串的’0’和’1’的个数不超过m和n。 明显的背包问题,常规的0/1背包是若干个商品,费用不超过某个值。这里的背包是若干个商品,费用不超过m和n,也就是有两个限制。本题可以用二维背包解决,和一维背包很类似,都是假设某个商品要还是不要,然后更新dp数组。只不过这里有两个限制条件,所以更新数组时需要两个for循环。 一维背包为了优化空间,可以用一维数组,然后从后往前填。二维背包为了优化空间,可以用二维数组,也从后往前填。 假设dp[i][j]表示在i个’0’和j个’1’的限制下,最多能取出的字符串(商品)数,则二维背包代码如下: [cpp] class Solution { public: int findMaxForm(vector<string>& strs, int m, int n) { vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); for (int i = 0; i < strs.size(); ++i) { vector<int> cnts(2, 0); for (int j = 0; j < strs[i].size(); ++j)++cnts[strs[i][j] – ‘0’]; for (int j = m; j >= cnts[0]; –j) { for (int k = n; k >= cnts[1]; –k) { dp[j][k] = max(dp[j][k], dp[j – cnts[0]][k – cnts[1]] + 1); } } } return dp[m][n]; } }; [/cpp] 本代码提交AC,用时66MS。]]>

LeetCode Increasing Subsequences

LeetCode Increasing Subsequences Given an integer array, your task is to find all the different possible increasing subsequences of the given array, and the length of an increasing subsequence should be at least 2 . Example:

Input: [4, 6, 7, 7]
Output: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
Note:
  1. The length of the given array will not exceed 15.
  2. The range of integer in the given array is [-100,100].
  3. The given array may contain duplicates, and two equal integers should also be considered as a special case of increasing sequence.

给定一个数组,求出改数组的所有递增子序列。注意两点,一是递增,二是子序列。 因为是求子序列,所以没必要对数组排序! 使用DFS,尝试将每个数加入cand,然后从下一个数开始,如果比cand的最后一个数大,则递归DFS。比较简单的思路。 需要注意的点是,结果不能有重复数组,所以需要满足第19、6行的条件,并且用set去重。 第19行条件:比如样例[4,4,6,7,7,8,9,10],第一个4为起点的DFS的结果已经包含第二个4为起点的DFS结果,所以需要第19行。 第6行条件:比如样例[4,4,6,7,7,8,9,10],从6开始DFS时,遇到第一个7和遇到第二个7后续DFS的结果都是一样的,所以需要第6行。 set去重:还会遇到这样的样例比如样例[4,6,7,4,8,9,10],第一个4DFS的结果包含了第二个4DFS的结果,但是这两个4不相邻,所以不能被第19行的条件防止重复,只能用set去重了。 代码如下: [cpp] class Solution { private: void dfs(set<vector<int>>& ans, vector<int>& nums, vector<int>& cand, int start){ if(cand.size() >= 2)ans.insert(cand); for(int i = start + 1; i < nums.size(); ++i){ if(i == start + 1 || nums[i] != nums[i – 1]){ if(nums[i] >= cand[cand.size() – 1]){ cand.push_back(nums[i]); dfs(ans, nums, cand, i); cand.pop_back(); } } } } public: vector<vector<int>> findSubsequences(vector<int>& nums) { set<vector<int>> ans; for(int i = 0; i < nums.size(); ++i){ if(i == 0 || nums[i] != nums[i-1]){ vector<int> cand = {nums[i]}; dfs(ans, nums, cand, i); } } return vector<vector<int>>(ans.begin(), ans.end()); } }; [/cpp] 本代码提交AC,用时269MS。]]>

LeetCode Non-overlapping Intervals

LeetCode Non-overlapping Intervals Given a collection of intervals, find the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping. Note:

  1. You may assume the interval’s end point is always bigger than its start point.
  2. Intervals like [1,2] and [2,3] have borders “touching” but they don’t overlap each other.
Example 1:
Input: [ [1,2], [2,3], [3,4], [1,3] ]
Output: 1
Explanation: [1,3] can be removed and the rest of intervals are non-overlapping.
Example 2:
Input: [ [1,2], [1,2], [1,2] ]
Output: 2
Explanation: You need to remove two [1,2] to make the rest of intervals non-overlapping.
Example 3:
Input: [ [1,2], [2,3] ]
Output: 0
Explanation: You don't need to remove any of the intervals since they're already non-overlapping.

给定一系列区间,问最少删除多少个区间能使得剩余区间没有重叠。区间[i,j]和[u,v]重叠是指u<j或者i<v,如果两个区间只是边界重叠比如j==u,则不算重叠。 我们可以使用贪心的策略求最多能保存多少个区间,然后用总数一减就得到最少删除的区间数。 首先对区间先后按start和end从小到大排序,然后从头开始遍历。假设我们上一次保留的区间的end是last_end,如果当前区间的intervals[i].start>=last_end,则说明区间i可以保留,更新last_end=intervals[i].end。如果intervals[i].start<last_end,则当前区间会和上一个区间重叠,需要删除一个区间,为了使留下来的区间更多,肯定要删除end大的区间,令last_end=min(last_end,intervals[i].end),通过保留end小的区间来间接模拟删除end大的区间。 当得到保留下来的不重叠区间个数ans后,用n-ans就是需要删除的区间数。完整代码如下: [cpp] bool comp(const Interval &i1, const Interval &i2){ return i1.start < i2.start || (i1.start == i2.start && i1.end < i2.end); } class Solution { public: int eraseOverlapIntervals(vector<Interval>& intervals) { if(intervals.empty()) return 0; sort(intervals.begin(), intervals.end(), comp); int ans = 1, n = intervals.size(), last_end = intervals[0].end; for(int i = 1; i < n; ++i){ if(intervals[i].start >= last_end){ ++ans; last_end = intervals[i].end; } else { last_end = min(last_end, intervals[i].end); } } return n – ans; } }; [/cpp] 本代码提交AC,用时9MS。]]>

LeetCode Evaluate Division

LeetCode Evaluate Division Equations are given in the format A / B = k, where A and B are variables represented as strings, and k is a real number (floating point number). Given some queries, return the answers. If the answer does not exist, return -1.0. Example: Given a / b = 2.0, b / c = 3.0. queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? . return [6.0, 0.5, -1.0, 1.0, -1.0 ]. The input is: vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries , where equations.size() == values.size(), and the values are positive. This represents the equations. Return vector<double>. According to the example above:

equations = [ ["a", "b"], ["b", "c"] ],
values = [2.0, 3.0],
queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ].
The input is always valid. You may assume that evaluating the queries will result in no division by zero and there is no contradiction.
给定一系列的除法表达式,要计算另一些除法表达式的结果。比如知道a/b=2.0,b/c=3.0,问a/c=?。 本题的解法是把一系列表达式转换为图,比如a/b=2.0转换为a指向b的边,边的值为2.0,同时b指向a的边的值为1/2.0。如果要求a/c,则在图中找一条a到c的边,并且把走过的边的值乘起来。 构建图的过程先把表达式中的string编码成int,然后把已知的直连边加入到图中。对于要查询的边start/target,首先判断一下两个端点是否在图中,如果至少有一个端点不在图中,则无法求值,返回-1。否则,在图中DFS或BFS找到target,走过的路径乘积就是结果。 题目说明所有values都是正数,所以不存在除0问题。 DFS代码如下: [cpp] class Solution { private: bool dfs(vector<vector<double>>& graph, int start, int x, int y, int target) { graph[start][y] = graph[start][x] * graph[x][y]; graph[y][start] = 1.0 / graph[start][y]; if(y == target)return true; int n = graph.size(); for(int i = 0; i < n; ++i){ if(graph[start][i] == 0 && graph[y][i] != 0){ if(dfs(graph, start, y, i, target))return true; } } return false; } double helper(vector<vector<double>>& graph, int start, int target) { if (start == target)return 1.0; else if (graph[start][target] != 0.0)return graph[start][target]; int n = graph.size(); for (int y = 0; y < n; ++y) { if (y == start)continue; if (graph[start][y] != 0.0) { if (dfs(graph, start, start, y, target))return graph[start][target]; } } return -1.0; } public: vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries) { int cnt = 0, n = equations.size(); unordered_map<string, int> hash; for (int i = 0; i < n; ++i) { if (hash.find(equations[i].first) == hash.end())hash[equations[i].first] = cnt++; if (hash.find(equations[i].second) == hash.end())hash[equations[i].second] = cnt++; } vector<vector<double>> graph(cnt, vector<double>(cnt, 0)); for (int i = 0; i < n; ++i) { int x = hash[equations[i].first], y = hash[equations[i].second]; graph[x][x] = 1; graph[y][y] = 1; graph[x][y] = values[i]; graph[y][x] = 1.0 / values[i]; } vector<double> ans; for (int i = 0; i < queries.size(); ++i) { if (hash.find(queries[i].first) == hash.end() || hash.find(queries[i].second) == hash.end())ans.push_back(-1.0); else { int x = hash[queries[i].first], y = hash[queries[i].second]; ans.push_back(helper(graph, x, y)); } } return ans; } }; [/cpp] 本代码提交AC,用时3MS。 BFS代码如下: [cpp] class Solution { private: double bfs(vector<vector<double>>& graph, int start, int target) { if (start == target)return 1.0; else if (graph[start][target] != 0.0)return graph[start][target]; int n = graph.size(); queue<int> q; for(int x = 0; x < n; ++x){ if(x != start && graph[start][x] != 0)q.push(x); } while(!q.empty()){ int x = q.front(); if(x == target) return graph[start][target]; q.pop(); for(int y = 0; y < n; ++y){ if(graph[start][y] == 0 && graph[x][y] != 0){ graph[start][y] = graph[start][x] * graph[x][y]; graph[y][start] = 1.0 / graph[start][y]; q.push(y); } } } return -1.0; } public: vector<double> calcEquation(vector<pair<string, string>> equations, vector<double>& values, vector<pair<string, string>> queries) { int cnt = 0, n = equations.size(); unordered_map<string, int> hash; for (int i = 0; i < n; ++i) { if (hash.find(equations[i].first) == hash.end())hash[equations[i].first] = cnt++; if (hash.find(equations[i].second) == hash.end())hash[equations[i].second] = cnt++; } vector<vector<double>> graph(cnt, vector<double>(cnt, 0)); for (int i = 0; i < n; ++i) { int x = hash[equations[i].first], y = hash[equations[i].second]; graph[x][x] = 1; graph[y][y] = 1; graph[x][y] = values[i]; graph[y][x] = 1.0 / values[i]; } vector<double> ans; for (int i = 0; i < queries.size(); ++i) { if (hash.find(queries[i].first) == hash.end() || hash.find(queries[i].second) == hash.end())ans.push_back(-1.0); else { int x = hash[queries[i].first], y = hash[queries[i].second]; ans.push_back(bfs(graph, x, y)); } } return ans; } }; [/cpp] 本代码提交AC,用时0MS,类似的题BFS显然要比DFS快,而且这题用BFS思路更清晰。]]>

LeetCode Guess Number Higher or Lower II

LeetCode Guess Number Higher or Lower II We are playing the Guess Game. The game is as follows: I pick a number from 1 to n. You have to guess which number I picked. Every time you guess wrong, I’ll tell you whether the number I picked is higher or lower. However, when you guess a particular number x, and you guess wrong, you pay $x. You win the game when you guess the number I picked. Example:

n = 10, I pick 8.
First round:  You guess 5, I tell you that it's higher. You pay $5.
Second round: You guess 7, I tell you that it's higher. You pay $7.
Third round:  You guess 9, I tell you that it's lower. You pay $9.
Game over. 8 is the number I picked.
You end up paying $5 + $7 + $9 = $21.
Given a particular n ≥ 1, find out how much money you need to have to guarantee a win. Hint:
  1. The best strategy to play the game is to minimize the maximum loss you could possibly face. Another strategy is to minimize the expected loss. Here, we are interested in the first scenario.
  2. Take a small example (n = 3). What do you end up paying in the worst case?
  3. Check out this article if you’re still stuck.
  4. The purely recursive implementation of minimax would be worthless for even a small n. You MUST use dynamic programming.
  5. As a follow-up, how would you modify your code to solve the problem of minimizing the expected loss, instead of the worst-case loss?

猜数字升级版。假设正确数字在[1,n]之间,每猜一个x,如果没猜中,则需要花费x元。问最少需要花费多少钱才能保证一定能猜中数字。 这是一个minmax问题,即最小化最大花费。使用动态规划解决,假设dp[l][r]表示在区间[l,r]猜数字时,一定能猜中的最小花费钱数。 假设在区间[l,r]猜数字,当猜x错误时,可以根据大小关系继续在[l,x-1]或[x+1,r]区间继续猜。假设已经知道[l,x-1]和[x+1,r]区间猜中的最小花费是dp[l][x-1]和dp[x+1][r],则猜x猜中的最小花费就是f(x)=x+max(dp[l][x-1],dp[x+1][r])。x在[l,r]遍历,取最小的f(x)就是区间[l,r]猜中的最小花费。 代码如下: [cpp] class Solution { private: int helper(vector<vector<int>>& dp, int l, int r) { if (l >= r)return 0; if (dp[l][r] != 0)return dp[l][r]; dp[l][r] = INT_MAX; for (int k = l; k <= r; ++k) { dp[l][r] = min(dp[l][r], k + max(helper(dp, l, k – 1), helper(dp, k + 1, r))); } return dp[l][r]; } public: int getMoneyAmount(int n) { vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0)); return helper(dp, 1, n); } }; [/cpp] 本代码提交AC,用时59MS。 还可以用迭代的思路。DP的思路就是在一个大的区间[l,r],尝试猜任意一个数x,然后取x+max(dp[l][x-1],dp[x+1][r])。但前提是小区间dp[l][x-1]和dp[x+1][r]之前已经算过了。 所以可以令l=n-1→1,r=l+1→n,x在[l,r)任意取值,因为x是从x-1遍历到x的,所以dp[l][x-1]算过了;又因为l是从n-1,n-2,..,x+1,x,x-1,…遍历的,所以dp[x+1][r]也是算过了,这样就能求f(x)=x+max(dp[l][x-1],dp[x+1][r])。 为什么不能令l=1→n-1,r=l+1→n呢,假设此时选择x,则虽然算过dp[l][x-1],但没算过dp[x+1][r],因为左端点的遍历顺序是1,2,…,l,…,x-1,x,x+1,…,左端点只遍历到l,还没遍历到x+1,所以dp[x+1][r]的值还没算好。 迭代的代码如下: [cpp] class Solution { public: int getMoneyAmount(int n) { vector<vector<int>> dp(n + 1, vector<int>(n + 1, 0)); for (int l = n – 1; l > 0; –l) { for (int r = l + 1; r <= n; ++r) { dp[l][r] = INT_MAX; for (int k = l; k < r; ++k) { dp[l][r] = min(dp[l][r], k + max(dp[l][k – 1], dp[k + 1][r])); } } } return dp[1][n]; } }; [/cpp] 本代码提交AC,用时9MS,快了好多。]]>

LeetCode Find Right Interval

LeetCode Find Right Interval Given a set of intervals, for each of the interval i, check if there exists an interval j whose start point is bigger than or equal to the end point of the interval i, which can be called that j is on the “right” of i. For any interval i, you need to store the minimum interval j’s index, which means that the interval j has the minimum start point to build the “right” relationship for interval i. If the interval j doesn’t exist, store -1 for the interval i. Finally, you need output the stored value of each interval as an array. Note:

  1. You may assume the interval’s end point is always bigger than its start point.
  2. You may assume none of these intervals have the same start point.
Example 1:
Input: [ [1,2] ]
Output: [-1]
Explanation: There is only one interval in the collection, so it outputs -1.
Example 2:
Input: [ [3,4], [2,3], [1,2] ]
Output: [-1, 0, 1]
Explanation: There is no satisfied "right" interval for [3,4].
For [2,3], the interval [3,4] has minimum-"right" start point;
For [1,2], the interval [2,3] has minimum-"right" start point.
Example 3:
Input: [ [1,4], [2,3], [3,4] ]
Output: [-1, 2, -1]
Explanation: There is no satisfied "right" interval for [1,4] and [3,4].
For [2,3], the interval [3,4] has minimum-"right" start point.

给定一系列区间[start,end],定义区间[i,j]的“右区间”为[u,v]使得u是第一个不小于j的数。要求输出每个区间的右区间下标,如果不存在右区间,则输出-1。 我的解法。因为要输出右区间在原数组中的下标,所以得保留每个区间在原数组中的下标。定义一个新的结构体MyInterval,用来保存区间以及该区间在原数组中的下标。对所有区间按start排序,然后遍历每一个区间[i,j],在已排序区间数组中找第一个start大于j的区间,取出它的下标作为区间[i,j]的结果。 代码如下: [cpp] typedef struct MyInterval { Interval inter; int idx; MyInterval(Interval inter_, int idx_) :inter(inter_), idx(idx_) {}; bool operator<(const MyInterval& myi) const{ return this->inter.start < myi.inter.start; // 所有start不相同 } }; class Solution2 { public: vector<int> findRightInterval(vector<Interval>& intervals) { int n = intervals.size(); vector<MyInterval> myvs; for (int i = 0; i < n; ++i) { MyInterval myint(intervals[i], i); myvs.push_back(myint); } sort(myvs.begin(), myvs.end()); vector<int> ans(n, -1); for (int i = 0; i < n; ++i) { for (int j = i + 1; j < n; ++j) { // 线性查找 if (myvs[j].inter.start >= myvs[i].inter.end) { ans[myvs[i].idx] = myvs[j].idx; break; } } } return ans; } }; [/cpp] 本代码提交AC,用时299MS。 上述代码还可优化,在已排序区间数组中找第一个start大于j的区间时,可以用二分搜索。 另外因为所有区间的start不相同,所以事先用hash记录start和原数组下标。这样只需要把所有区间的start抽出来排序,然后二分查找每一个end。代码如下: [cpp] class Solution { private: int binsearch(const vector<int>& starts, int target) { int l = 0, r = starts.size() – 1; while (l <= r) { int m = l + (r – l) / 2; if (starts[m] == target)return starts[m]; if (starts[m] < target)l = m + 1; else r = m – 1; } return starts[l]; } public: vector<int> findRightInterval(vector<Interval>& intervals) { int n = intervals.size(); vector<int> starts; unordered_map<int, int> hash; for (int i = 0; i < n; ++i) { starts.push_back(intervals[i].start); hash[intervals[i].start] = i; } sort(starts.begin(), starts.end()); vector<int> ans(n, -1); for (int i = 0; i < n; ++i) { if (intervals[i].end <= starts[n – 1]) { int t = binsearch(starts, intervals[i].end); ans[i] = hash[t]; } } return ans; } }; [/cpp] 本代码提交AC,用时96MS,快了不少。]]>

LeetCode Lexicographical Numbers

LeetCode Lexicographical Numbers Given an integer n, return 1 – n in lexicographical order. For example, given 13, return: [1,10,11,12,13,2,3,4,5,6,7,8,9]. Please optimize your algorithm to use less time and space. The input size may be as large as 5,000,000.


要求把1~n的n个数以字典序的方式输出。 明显不能先把int转换为string然后sort,这样肯定会超时。讨论区有一个大神的解法很厉害,学习一下,他通过枚举的方法一次性把1~n的字典序数组生成了。 假设我们枚举到当前数是cur,则下一个字典序的数是哪个呢。首先,下一个最小字典序的数肯定是末尾加0的,即cur*10。如果cur*10超过范围,则下一个最小字典序的数肯定是个数为加1,即cur+1。如果cur+1也超过范围了,则下一个最小字典序的数应该是十位数加1,个位数变为0,并且把个位删掉,即cur=cur/10+1。 代码如下: [cpp] class Solution { public: vector<int> lexicalOrder(int n) { vector<int> ans; int cur = 1; for (int i = 1; i <= n; ++i) { ans.push_back(cur); if (cur * 10 <= n)cur *= 10; else if (cur % 10 != 9 && cur + 1 <= n)++cur; else { while ((cur / 10) % 10 == 9)cur /= 10; cur = cur / 10 + 1; } } return ans; } }; [/cpp] 本代码提交AC,用时168MS。 举个例子,假设cur=45,则下一个数首先是450;如果450超过n,则下一个数应该是46;如果46也超过范围,则下一个数应该是5。如果遇到cur=499,且跳到了第2个if,++cur==500,但是明显下一个数应该是5,所以不能进入第2个if,进入到第3个if,不断把末尾的9去掉,然后高位加1变为5。]]>

LeetCode Bulb Switcher

LeetCode Bulb Switcher There are n bulbs that are initially off. You first turn on all the bulbs. Then, you turn off every second bulb. On the third round, you toggle every third bulb (turning on if it’s off or turning off if it’s on). For the ith round, you toggle every i bulb. For the nth round, you only toggle the last bulb. Find how many bulbs are on after n rounds. Example:

Given n = 3.
At first, the three bulbs are [off, off, off].
After first round, the three bulbs are [on, on, on].
After second round, the three bulbs are [on, off, on].
After third round, the three bulbs are [on, off, off].
So you should return 1, because there is only one bulb is on.

给定n盏灯,第一轮是所有灯都是亮的;第二轮每两盏灯触发(亮变灭或灭变亮)一盏灯;第三轮每三盏灯触发一盏灯;如此循环直到第n轮只触发最后一盏灯。问最后有多少盏灯是亮着的。 首先模拟一把: [cpp] class Solution { public: int bulbSwitch(int n) { if (n == 0)return 0; vector<int> bulbs(n + 1, 1); for (int i = 2; i <= n; ++i) { for (int j = 1; j*i <= n; ++j)bulbs[j*i] = !bulbs[j*i]; } int ans = 0; for (int i = 1; i <= n; ++i)if (bulbs[i])++ans; return ans; } }; [/cpp] 本代码提交TLE,过不了大数据集。 然后就把前10的结果列出来了,在OEIS中找到了序列对应的公式:a(n) = floor(sqrt(n))。后来看了网上的题解,得到了这个通项公式的解释: 如果第i盏灯的i的因子有奇数个,则这盏灯最终是亮的,比如9有1,3,9这3个因子,则第9盏灯会分别在第1、3、9轮被触发,由于第1轮是变亮,经过奇数轮之后还是亮的。如果i的因子有偶数个,则类似的分析可知这盏灯最终是灭的。 那么哪些数的因子个数是奇数呢,只有完全平方数的因子个数是奇数,因为非完全平方数的因子都是成对出现的,比如8=1*8=2*4=4*2,而完全平方数有两个因子是重复的9=1*9=3*3,3相当于1个因子。 1~n有floor(sqrt(n))个完全平方数,所以结果就是floor(sqrt(n))。 代码如下: [cpp] class Solution { public: int bulbSwitch(int n) { return floor(sqrt(n)); } }; [/cpp] 本代码提交AC,用时3MS。]]>