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