Check Array Formation Through Concatenation 的 Leetcode 解决方案
Leetcode solution of Check Array Formation Through Concatenation
我已经测试了这个问题的基于字符串和基于映射的实现。第一个运行时间为 12 毫秒,而第二个运行时间为 1 毫秒,两者几乎相同 space。并且这两种风格的实现结构看起来是一样的。
所以,我想知道为什么两者之间存在很大的运行时差异。
以下是两种实现方式:
class Solution {
public boolean canFormArray(int[] arr, int[][] pieces) {
// string based
String str = "";
for (int i=0; i<arr.length; i++) {
str = str + arr[i] + "#";
}
for (int i=0; i<pieces.length; i++) {
String str2 = "";
for (int j=0; j<pieces[i].length; j++) {
str2 = str2 + pieces[i][j] + "#";
}
if (!str.contains(str2)) {
return false;
}
}
return true;
// map based
HashMap<Integer, Integer> map = new HashMap<>();
for (int i=0; i<arr.length; i++) {
map.put(arr[i], i);
}
for (int i=0; i<pieces.length; i++) {
if (!map.containsKey(pieces[i][0])) {
return false;
}
int index = map.get(pieces[i][0]);
for (int j=1; j<pieces[i].length; j++) {
if (!map.containsKey(pieces[i][j])) {
return false;
}
if (map.get(pieces[i][j]) != index+1) {
return false;
}
index++;
}
}
return true;
}
}
猜测 str = str + arr[i] + "#";
和 str2 = str2 + pieces[i][j] + "#";
正在循环内创建新字符串。虽然创建对象不再像过去那样是一项重量级任务,但最好还是避免在循环中执行此操作。
为避免这种情况,在循环外创建 StringBuilder,在循环内使用 append
方法,并在循环完成后使用 toString()
。
str.contains(str2) 在最坏情况下的时间复杂度为 O(nm)。
而 map.containsKey(pieces[i][0]) 的时间复杂度为 O(1)
请同时查看此答案以了解更多详细信息
Time complexity of String.contains()
- 类似的方法,这会传入 Java:
public class Solution {
public static final boolean canFormArray(
final int[] arr,
final int[][] pieces
) {
StringBuilder sb = new StringBuilder();
for (int num : arr) {
sb.append(num);
sb.append("#");
}
for (int i = 0; i < pieces.length; i++) {
StringBuilder res = new StringBuilder();
for (int j = 0; j < pieces[i].length; j++) {
res.append(String.valueOf(pieces[i][j]));
res.append("#");
}
if (!sb.toString().contains(res.toString())) {
return false;
}
}
return true;
}
}
- 更好的方法是使用
HashMap
,这里是 C++ 版本:
// Most of headers are already included;
// Can be removed;
#include <iostream>
#include <vector>
#include <unordered_map>
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
struct Solution {
static const bool canFormArray(
const std::vector<int>& arr,
const std::vector<std::vector<int>>& pieces
) {
std::unordered_map<unsigned int, std::vector<int>> starts_partitions;
for (const auto& piece : pieces) {
starts_partitions[piece[0]] = piece;
}
std::vector<int> form_array;
for (const auto& num : arr) {
form_array.insert(
std::end(form_array),
std::begin(starts_partitions[num]),
std::end(starts_partitions[num])
);
}
return form_array == arr;
}
};
// int main() {
// std::cout << Solution().canFormArray({85}, {{85}}) << "\n";
// std::cout << Solution().canFormArray({15, 88}, {{88}, {15}}) << "\n";
// std::cout << Solution().canFormArray({49, 18, 16}, {{16, 18, 49}}) << "\n";
// std::cout << Solution().canFormArray({91, 4, 64, 78}, {{78}, {4, 64}, {91}}) << "\n";
// std::cout << Solution().canFormArray({1, 3, 5, 7}, {{2, 4, 6, 8}}) << "\n";
// };
- 如果我们只查看 Python 版本会更容易理解:
class Solution:
def canFormArray(self, arr: List[int], pieces: List[List[int]]) -> bool:
starts_pieces = {piece[0]: piece for piece in pieces}
form_array = []
for num in arr:
form_array += starts_pieces.get(num, [])
return form_array == arr
我已经测试了这个问题的基于字符串和基于映射的实现。第一个运行时间为 12 毫秒,而第二个运行时间为 1 毫秒,两者几乎相同 space。并且这两种风格的实现结构看起来是一样的。 所以,我想知道为什么两者之间存在很大的运行时差异。 以下是两种实现方式:
class Solution {
public boolean canFormArray(int[] arr, int[][] pieces) {
// string based
String str = "";
for (int i=0; i<arr.length; i++) {
str = str + arr[i] + "#";
}
for (int i=0; i<pieces.length; i++) {
String str2 = "";
for (int j=0; j<pieces[i].length; j++) {
str2 = str2 + pieces[i][j] + "#";
}
if (!str.contains(str2)) {
return false;
}
}
return true;
// map based
HashMap<Integer, Integer> map = new HashMap<>();
for (int i=0; i<arr.length; i++) {
map.put(arr[i], i);
}
for (int i=0; i<pieces.length; i++) {
if (!map.containsKey(pieces[i][0])) {
return false;
}
int index = map.get(pieces[i][0]);
for (int j=1; j<pieces[i].length; j++) {
if (!map.containsKey(pieces[i][j])) {
return false;
}
if (map.get(pieces[i][j]) != index+1) {
return false;
}
index++;
}
}
return true;
}
}
猜测 str = str + arr[i] + "#";
和 str2 = str2 + pieces[i][j] + "#";
正在循环内创建新字符串。虽然创建对象不再像过去那样是一项重量级任务,但最好还是避免在循环中执行此操作。
为避免这种情况,在循环外创建 StringBuilder,在循环内使用 append
方法,并在循环完成后使用 toString()
。
str.contains(str2) 在最坏情况下的时间复杂度为 O(nm)。
而 map.containsKey(pieces[i][0]) 的时间复杂度为 O(1)
请同时查看此答案以了解更多详细信息 Time complexity of String.contains()
- 类似的方法,这会传入 Java:
public class Solution {
public static final boolean canFormArray(
final int[] arr,
final int[][] pieces
) {
StringBuilder sb = new StringBuilder();
for (int num : arr) {
sb.append(num);
sb.append("#");
}
for (int i = 0; i < pieces.length; i++) {
StringBuilder res = new StringBuilder();
for (int j = 0; j < pieces[i].length; j++) {
res.append(String.valueOf(pieces[i][j]));
res.append("#");
}
if (!sb.toString().contains(res.toString())) {
return false;
}
}
return true;
}
}
- 更好的方法是使用
HashMap
,这里是 C++ 版本:
// Most of headers are already included;
// Can be removed;
#include <iostream>
#include <vector>
#include <unordered_map>
// The following block might slightly improve the execution time;
// Can be removed;
static const auto __optimize__ = []() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
struct Solution {
static const bool canFormArray(
const std::vector<int>& arr,
const std::vector<std::vector<int>>& pieces
) {
std::unordered_map<unsigned int, std::vector<int>> starts_partitions;
for (const auto& piece : pieces) {
starts_partitions[piece[0]] = piece;
}
std::vector<int> form_array;
for (const auto& num : arr) {
form_array.insert(
std::end(form_array),
std::begin(starts_partitions[num]),
std::end(starts_partitions[num])
);
}
return form_array == arr;
}
};
// int main() {
// std::cout << Solution().canFormArray({85}, {{85}}) << "\n";
// std::cout << Solution().canFormArray({15, 88}, {{88}, {15}}) << "\n";
// std::cout << Solution().canFormArray({49, 18, 16}, {{16, 18, 49}}) << "\n";
// std::cout << Solution().canFormArray({91, 4, 64, 78}, {{78}, {4, 64}, {91}}) << "\n";
// std::cout << Solution().canFormArray({1, 3, 5, 7}, {{2, 4, 6, 8}}) << "\n";
// };
- 如果我们只查看 Python 版本会更容易理解:
class Solution:
def canFormArray(self, arr: List[int], pieces: List[List[int]]) -> bool:
starts_pieces = {piece[0]: piece for piece in pieces}
form_array = []
for num in arr:
form_array += starts_pieces.get(num, [])
return form_array == arr