Leetcode ซูโดกุที่ถูกต้อง

Nov 18 2020

ลิงค์ที่นี่

ฉันจะรวมโซลูชันใน Python และ C ++ และคุณสามารถตรวจสอบได้ ฉันสนใจที่จะตรวจสอบโค้ด C ++ ซึ่งเป็นสิ่งที่ฉันเพิ่งเริ่มเรียนรู้ ผู้ที่ไม่ทราบ C ++ สามารถตรวจสอบโค้ด Python ได้ โซลูชันทั้งสองใช้ตรรกะที่คล้ายกันดังนั้นการตรวจสอบจะนำไปใช้กับ


คำชี้แจงปัญหา

ตรวจสอบว่าบอร์ดซูโดกุ 9 x 9 ถูกต้องหรือไม่ เฉพาะเซลล์ที่เติมเท่านั้นที่ต้องได้รับการตรวจสอบตามกฎต่อไปนี้:

  • แต่ละแถวต้องมีตัวเลข 1-9 โดยไม่มีการซ้ำ แต่ละคอลัมน์
  • ต้องมีตัวเลข 1-9 โดยไม่มีการซ้ำ แต่ละเก้า 3 x
  • 3 ช่องย่อยของตารางต้องมีตัวเลข 1-9 โดยไม่มีการทำซ้ำ

บันทึก:

บอร์ด Sudoku (เติมบางส่วน) อาจใช้ได้ แต่ไม่จำเป็นต้องแก้ได้ เฉพาะเซลล์ที่เติมเท่านั้นที่ต้องได้รับการตรวจสอบตามกฎที่กล่าวถึง

ตัวอย่างที่ 1:

Input: board = 
[["5","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: true

ตัวอย่างที่ 2:

Input: board = 
[["8","3",".",".","7",".",".",".","."]
,["6",".",".","1","9","5",".",".","."]
,[".","9","8",".",".",".",".","6","."]
,["8",".",".",".","6",".",".",".","3"]
,["4",".",".","8",".","3",".",".","1"]
,["7",".",".",".","2",".",".",".","6"]
,[".","6",".",".",".",".","2","8","."]
,[".",".",".","4","1","9",".",".","5"]
,[".",".",".",".","8",".",".","7","9"]]
Output: false
Explanation: Same as Example 1, except with the 5 in the top left corner being modified to 8. Since there are two 8's in the top left 3x3 sub-box, it is invalid.

valid_sudoku.py

def is_valid(board, empty_value='.', b_size=3):
    seen = set()
    size = b_size * b_size
    for row in range(size):
        for col in range(size):
            if (value := board[row][col]) == empty_value:
                continue
            r = f'0{row}{value}'
            c = f'1{col}{value}'
            b = f'2{row // b_size}{col // b_size}{value}'
            if r in seen or c in seen or b in seen:
                return False
            seen.update({r, c, b})
    return True


if __name__ == '__main__':
    g = [
        ["5", "3", ".", ".", "7", "5", ".", ".", "."],
        ["6", ".", ".", "1", "9", "5", ".", ".", "."],
        [".", "9", "8", ".", ".", ".", ".", "6", "."],
        ["8", ".", ".", ".", "6", ".", ".", ".", "3"],
        ["4", ".", ".", "8", ".", "3", ".", ".", "1"],
        ["7", ".", ".", ".", "2", ".", ".", ".", "6"],
        [".", "6", ".", ".", ".", ".", "2", "8", "."],
        [".", ".", ".", "4", "1", "9", ".", ".", "5"],
        [".", ".", ".", ".", "8", ".", ".", "7", "9"],
    ]
    print(is_valid(g))

สถิติ:

Runtime: 92 ms, faster than 81.70% of Python3 online submissions for Valid Sudoku.
Memory Usage: 14.1 MB, less than 73.95% of Python3 online submissions for Valid Sudoku.

นี่เป็นทางเลือกอื่นโดยใช้ numpy ซึ่งสั้นกว่าและอ่านง่ายกว่า แต่ช้ากว่า:

import numpy as np


def is_valid(board, size=3, empty_value='.'):
    board = np.array(board)
    blocks = board.reshape(4 * [size]).transpose(0, 2, 1, 3).reshape(2 * [size * size])
    for grid in [board, board.T, blocks]:
        for line in grid:
            non_empty = line[line != empty_value]
            if not len(non_empty) == len(set(non_empty)):
                return False
    return True

สถิติ:

Runtime: 172 ms, faster than 5.19% of Python3 online submissions for Valid Sudoku.
Memory Usage: 30.2 MB, less than 11.10% of Python3 online submissions for Valid Sudoku.

valid_sudoku.h

#ifndef LEETCODE_VALID_SUDOKU_H
#define LEETCODE_VALID_SUDOKU_H

#include <string_view>
#include <unordered_set>

bool sudoku_check_update(const size_t &row, const size_t &col, const char &value,
                         const int &block_size,
                         std::unordered_set<std::string_view> &seen);

bool sudoku_check(const std::vector<std::vector<char>> &board,
                  const char &empty_value = '.');

void test1();

#endif //LEETCODE_VALID_SUDOKU_H

valid_sudoku.cpp

#include <iostream>
#include <vector>
#include <string_view>
#include <cmath>
#include <unordered_set>


bool sudoku_check_update(const size_t &row, const size_t &col, const char &value,
                         const int &block_size,
                         std::unordered_set<std::string_view> &seen) {
    std::string_view r, c, b;
    r = "0-" + std::to_string(row) + value;
    c = "1-" + std::to_string(col) + value;
    b = "2-" + std::to_string(row / block_size) + std::to_string(col / block_size) +
        value;
    for (const auto &seen_id: {r, c, b}) {
        if (seen.find(seen_id) != seen.end())
            return false;
        seen.insert(seen_id);
    }
    return true;
}


bool sudoku_check(const std::vector<std::vector<char>> &board,
                  const char &empty_value = '.') {
    std::unordered_set<std::string_view> seen;
    const auto row_size = board.size();
    const int block_size = std::sqrt(row_size);
    for (size_t row = 0; row < row_size; ++row) {
        for (size_t col = 0; col < row_size; ++col) {
            auto value = board[row][col];
            if (value == empty_value)
                continue;
            if (!sudoku_check_update(row, col, value, block_size, seen))
                return false;
        }
    }
    return true;
}


void test1() {
    std::vector<std::vector<char>> v = {
            {'5', '3', '.', '.', '7', '.', '.', '.', '.'},
            {'6', '.', '.', '1', '9', '5', '.', '.', '.'},
            {'.', '9', '8', '.', '.', '.', '.', '6', '.'},
            {'8', '.', '.', '.', '6', '.', '.', '.', '3'},
            {'4', '.', '.', '8', '.', '3', '.', '.', '1'},
            {'7', '.', '.', '.', '2', '.', '.', '.', '6'},
            {'.', '6', '.', '.', '.', '.', '2', '8', '.'},
            {'.', '.', '.', '4', '1', '9', '.', '.', '5'},
            {'.', '.', '.', '.', '8', '.', '.', '7', '9'}
    };
    std::cout << sudoku_check(v);
}

สถิติ:

Runtime: 48 ms, faster than 17.98% of C++ online submissions for Valid Sudoku.
Memory Usage: 20.4 MB, less than 22.55% of C++ online submissions for Valid Sudoku.

คำตอบ

8 Edward Nov 19 2020 at 02:14

คำแนะนำบางประการเกี่ยวกับวิธีที่คุณจะสามารถปรับปรุงโค้ดของคุณได้

เวอร์ชัน C ++

ใช้#includes ที่จำเป็นทั้งหมด

ชนิดstd::vector<std::vector<char>>ถูกใช้ในคำจำกัดความของsudoku_check()ในไฟล์ส่วนหัว แต่#include <vector>ไม่มีอยู่ในรายการรวมที่นั่น

ย่อส่วนอินเทอร์เฟซ

.hไฟล์คือการประกาศของอินเตอร์เฟซซอฟแวร์ของคุณ .cppคือการดำเนินงานของอินเตอร์เฟซที่ เป็นการออกแบบที่ดีในการย่อส่วนอินเทอร์เฟซให้เหลือเพียงที่โปรแกรมภายนอกต้องการ สำหรับเหตุผลที่ฉันจะลบsudoku_check_update()และtest1()ฟังก์ชั่นและใช้เพียงนี้:

#ifndef LEETCODE_VALID_SUDOKU_H
#define LEETCODE_VALID_SUDOKU_H

#include <vector>

bool sudoku_check(const std::vector<std::vector<char>> &board,
                  const char &empty_value = '.');

#endif //LEETCODE_VALID_SUDOKU_H

การนำไปใช้งานควรมีส่วนหัวของอินเทอร์เฟซ

เนื่องจากชื่อของส่วนนี้ระบุการใช้งานควรมีส่วนหัวของอินเทอร์เฟซ สิ่งนี้ทำให้มั่นใจได้ว่าอินเทอร์เฟซและการใช้งานตรงกันและขจัดข้อผิดพลาด หากเราทำเช่นนั้นในกรณีนี้เราจะเห็นว่าค่าเริ่มต้นสำหรับempty_valueถูกประกาศสองครั้ง ควรประกาศเพียงครั้งเดียวในไฟล์ส่วนหัว

สร้างฟังก์ชันท้องถิ่น static

ด้วยอินเทอร์เฟซที่เล็กกว่าดังที่ได้รับการสนับสนุนข้างต้นsudoku_check_updateฟังก์ชันนี้จะกลายเป็นรายละเอียดการใช้งานที่ใช้ภายใน.cppไฟล์เท่านั้น ด้วยเหตุนี้จึงควรทำstaticเพื่อให้คอมไพเลอร์รู้ว่าการอินไลน์ฟังก์ชันนั้นปลอดภัย

คีย์เวิร์ดstaticเมื่อใช้กับการประกาศฟังก์ชันระบุว่าการเชื่อมโยงเป็นแบบภายใน กล่าวอีกนัยหนึ่งก็หมายความว่าไม่มีสิ่งใดนอกไฟล์นั้นที่สามารถเข้าถึงฟังก์ชันได้ สิ่งนี้มีประโยชน์สำหรับคอมไพลเลอร์ที่จะต้องทราบเนื่องจากตัวอย่างเช่นหากstaticฟังก์ชันถูกใช้เพียงครั้งเดียวและ / หรือมีขนาดเล็กคอมไพเลอร์จะมีตัวเลือกในการใส่โค้ดแบบอินไลน์ นั่นคือแทนที่จะเป็นภาษาแอสเซมบลีตามปกติcall... retคำแนะนำในการข้ามไปยังรูทีนย่อยและกลับมาจากมันคอมไพลเลอร์สามารถใส่โค้ดสำหรับฟังก์ชันได้โดยตรงที่ตำแหน่งนั้นช่วยประหยัดค่าใช้จ่ายในการคำนวณของคำสั่งเหล่านั้นและช่วยให้มั่นใจในแคช การคาดคะเนถูกต้อง (เนื่องจากโดยปกติแคชจะใช้ประโยชน์จากตำแหน่งที่ตั้งของการอ้างอิง )

อ่านเพิ่มเติมเกี่ยวกับตัวระบุคลาสพื้นที่จัดเก็บเพื่อทำความเข้าใจสิ่งที่staticทำในบริบทอื่น ๆ และตัวระบุการประกาศโดยทั่วไปสำหรับคำอธิบายconstexprและอื่น ๆ

แก้ไขข้อบกพร่อง!

รหัสนี้ใช้string_viewอย่างไม่เหมาะสม A std::string_viewคือตัวชี้ไปยังสตริงที่มีอยู่ std::string_viewแต่สายของคุณจะประกอบด้วยและลบแบบไดนามิกดังนั้นนี่คือการใช้งานที่ไม่ถูกต้อง หากคุณแทนที่อินสแตนซ์ทั้งหมดstring_viewด้วยstringโปรแกรมจะทำงานได้

ปัญหาหน่วยความจำเช่นนี้และข้อผิดพลาดพร้อมกันเป็นปัญหาที่ยากที่สุดสำหรับโปรแกรมเมอร์ในการตรวจจับและแก้ไข เมื่อคุณได้รับประสบการณ์มากขึ้นคุณจะพบว่าความสามารถในการมองเห็นปัญหาเหล่านี้และหลีกเลี่ยงปัญหาเหล่านี้จะเกิดขึ้นในทันที มีหลายวิธีในการค้นหาข้อผิดพลาดดังกล่าว ดูคลาสง่ายๆในการตรวจจับการรั่วไหลสำหรับบางคน

เขียนฟังก์ชันการทดสอบที่ดีขึ้น

จุดบกพร่องที่กล่าวถึงข้างต้นสามารถค้นพบได้อย่างง่ายดายโดยการเรียกใช้ฟังก์ชันหลาย ๆ ครั้งด้วยอินพุตที่แตกต่างกัน บางทีคุณอาจมีฟังก์ชั่นการทดสอบที่กว้างขวางกว่านี้อยู่แล้ว แต่ถ้าไม่เป็นเช่นนั้นฉันขอแนะนำให้สร้างและนำไปใช้

ใช้โครงสร้างข้อมูลที่มีประสิทธิภาพ

หากเป้าหมายของรหัสนี้มีประสิทธิภาพทั้งในแง่ของเวลาทำงานและหน่วยความจำก็มีการปรับปรุงมากมายที่สามารถทำได้ ประการแรกโครงสร้างข้อมูลstd::unordered_set<std::string_view>ไม่เหมาะสม เมื่อใดก็ตามที่เรากำลังดำเนินการเพิ่มประสิทธิภาพการวัดผลก็มีประโยชน์ ดังนั้นผมจึงเขียนโปรแกรมการทดสอบที่ง่ายมากขึ้นอยู่กับฉันเทมเพลตนาฬิกาจับเวลา ที่นี่:

#include "valid_sudoku.h"
#include "stopwatch.h"
#include <iostream>
#include <vector>
#include <string>

int main(int argc, char* argv[]) {
    std::vector<std::vector<char>> v = {
            {'5', '3', '.', '.', '7', '.', '.', '.', '.'},
            {'6', '.', '.', '1', '9', '5', '.', '.', '.'},
            {'.', '9', '8', '.', '.', '.', '.', '6', '.'},
            {'8', '.', '.', '.', '6', '.', '.', '.', '3'},
            {'4', '.', '.', '8', '.', '3', '.', '.', '1'},
            {'7', '.', '.', '.', '2', '.', '.', '.', '6'},
            {'.', '6', '.', '.', '.', '.', '2', '8', '.'},
            {'.', '.', '.', '4', '1', '9', '.', '.', '5'},
            {'.', '.', '.', '.', '8', '.', '.', '7', '9'}
    };
    if (argc != 2) {
        std::cout << "Usage: " << argv[0] << " num_trials\n";
        return 1;
    }
    auto iterations = std::stoul(argv[1]);

    Stopwatch<> timer{};

    bool valid{true};
    for (auto i{iterations}; i; --i) {
        valid &= sudoku_check(v);
    }

    auto elapsed{timer.stop()};
    if (!valid) {
        std::cout << "The program failed!\n";
        return 2;
    }
    std::cout << iterations << " trials took " << elapsed << " microseconds\n"
        " for an average of " << elapsed/iterations << " microseconds/trial\n";
}

เมื่อฉันรันสิ่งนี้บนเครื่องของฉันด้วยการทดลอง 1,000,000 ครั้ง (โดยที่ข้อบกพร่องที่ระบุไว้ข้างต้นได้รับการแก้ไขตามที่อธิบายไว้) นี่คือผลลัพธ์ที่ฉันได้รับ:

การทดลอง 1000000 ครั้งใช้เวลา 1.44351e + 07 ไมโครวินาทีโดยเฉลี่ย 14.4351 ไมโครวินาที / การทดลอง

ทีนี้ลองนึกถึงโครงสร้างข้อมูลที่มีประสิทธิภาพมากขึ้น แทนที่จะเป็นunordered_setเราอาจใช้ชุดของอาร์เรย์คงที่ มีเก้าแถวเก้าคอลัมน์และเก้าตารางย่อย แต่ละรายการมีตัวเลขหรือไม่มี สำหรับฉันนั่นแสดงให้เห็นว่าเราสามารถใช้วัตถุเช่นนี้:

using SeenType = std::array<std::array<std::array<bool, 9>, 9>, 3>;

ซึ่งประกอบด้วย 3 ประเภท (แถวคอลัมน์จัตุรัสย่อย) และภายในแต่ละชุดมี 9 คอลเล็กชัน 9 บิต หนึ่งบิตสำหรับแต่ละหมายเลข ลองเขียนฟังก์ชันใหม่เพื่อใช้สิ่งนี้:

static bool sudoku_check_update(std::size_t row, std::size_t col, 
        char value, SeenType &seen) {
    static constexpr std::size_t block_size{3};
    static_assert(block_size * block_size == row_size, "block_size must be the square root of row_size");
    const std::size_t block = col / block_size + block_size * (row / block_size);
    std::size_t dim{0};
    value -= '1';   // adjust from digits '1'-'9' to indices 0-8.
    for (const auto &seen_id: {row, col, block}) {
        if (seen[dim][seen_id][value])
            return false;
        seen[dim][seen_id][value] = true;
        ++dim;
    }
    return true;
}

ตอนนี้เรียกใช้โปรแกรมอีกครั้งด้วยการทดลองใช้หนึ่งล้านครั้งเหมือนเดิม:

การทดลอง 1000000 ครั้งใช้เวลา 562153 ไมโครวินาทีโดยเฉลี่ย 0.562153 ไมโครวินาที / การทดลอง

ดังนั้นการเปลี่ยนแปลงที่หนึ่งทำสิ่งที่25x ได้เร็วขึ้น เรายังสามารถใช้ความจริงที่ว่ามิติข้อมูลนั้นใช้std::array<std::array<char, 9>, 9>แทนเวกเตอร์และใช้constexprสำหรับมิติเหล่านั้นได้ เมื่อทำการเปลี่ยนแปลงเช่นกันเราจะได้รับสิ่งนี้:

การทดลอง 1000000 ครั้งใช้เวลา 160808 ไมโครวินาทีโดยเฉลี่ย 0.160808 ไมโครวินาที / การทดลอง

ตอนนี้เร็วขึ้น 90เท่า

ต้องการการ{}เริ่มต้นสไตล์

คุณอาจสังเกตว่าโค้ดที่ฉันเขียนมีแนวโน้มที่จะใช้{}- สไตล์การเริ่มต้น มีสาเหตุหลายประการรวมถึงข้อเท็จจริงที่ว่าเมื่อคุณเห็นมันเป็นการเริ่มต้นเสมอและไม่สามารถเข้าใจผิดว่าเป็นการเรียกใช้ฟังก์ชัน ดูES.23สำหรับรายละเอียดเพิ่มเติม

ส่งผ่านค่ามากกว่าการอ้างอิงสำหรับชนิดข้อมูลสั้น ๆ

แทนที่จะผ่านไปconst size_t &colหรือconst char& valueโดยทั่วไปแล้วจะดีกว่าที่จะส่งผ่านค่าเหล่านั้น สิ่งนี้มักเป็นประโยชน์เนื่องจากตัวชี้มีแนวโน้มที่จะยาวกว่าสิ่งที่ชี้ไปและเนื่องจากช่วยให้สามารถกำจัดการค้นหาทิศทางและหน่วยความจำได้

ย้ายการคำนวณจากรันไทม์ไปยังคอมไพล์เวลาที่ใช้งานได้จริง

อาจใช้เวลาไม่มาก แต่บรรทัดนี้ไม่เร็วเท่าที่ควร:

const int block_size = std::sqrt(row_size);

สิ่งนี้คือการแปลงrow_sizeเป็น a doubleเรียกใช้sqrtฟังก์ชันจุดลอยตัวและแปลงdoubleกลับเป็นintไฟล์. ในทางตรงกันข้ามเราสามารถเขียนสิ่งนี้ได้:

constexpr std::size_t block_size{3};

ตอนนี้ใช้เวลาไม่นานเลยในรันไทม์เนื่องจากทราบค่าในเวลาคอมไพล์ นอกจากนี้ยังช่วยลดการต้องผ่านค่าและดังที่กล่าวมาข้างต้นคำจำกัดความของมันสามารถวางไว้ที่เดียวที่จำเป็นจริงซึ่งอยู่ในsudoku_check_updateฟังก์ชัน

โดยทั่วไปเราชอบที่จะย้ายสิ่งต่างๆจากรันไทม์เพื่อรวบรวมเวลาด้วยเหตุผลสามประการ:

  1. โดยทั่วไปโปรแกรมจะทำงานมากกว่าที่คอมไพล์ดังนั้นเราจึงปรับให้เหมาะสมกับเหตุการณ์ที่เกิดขึ้นบ่อยกว่า
  2. ยิ่งเราตรวจพบจุดบกพร่องเร็วเท่าไหร่ก็ยิ่งถูกและแก้ไขได้ง่ายขึ้นเท่านั้น
  3. มีแนวโน้มที่จะทำให้ซอฟต์แวร์มีขนาดเล็กลงและภายในง่ายขึ้นซึ่งจะช่วยเพิ่มความเร็วในการโหลดประสิทธิภาพแคชและซอฟต์แวร์ที่เรียบง่ายมีแนวโน้มที่จะปรับปรุงคุณภาพ

เวอร์ชัน Python

หลีกเลี่ยงcontinueโดยการปรับโครงสร้างลูป

ไม่มีอะไรที่ยิ่งผิดปกติกับการใช้งานของผู้ประกอบการวอลรัส continueแต่ดูเหมือนว่าจะมีเหตุผลเพียงเล็กน้อยที่จะไม่กลับความรู้สึกของการเปรียบเทียบและเพียงแค่ขั้นตอนการปรับปรุงมากกว่าการใช้ ไม่มีผลต่อประสิทธิภาพการทำงาน แต่ช่วยให้ผู้อ่านโค้ดเข้าใจกระบวนการทำงานของโปรแกรม ฉันมักจะใส่ต้น "bailout" ข้อต้นในการทำงานได้อย่างรวดเร็วปฏิเสธเงื่อนไขที่ไม่ถูกต้อง แต่หลีกเลี่ยงcontinueในลูป; ท้ายที่สุดมันเป็นคำถามเกี่ยวกับความสามารถในการอ่านและรูปแบบใน C ++ หรือ Python

ใช้โครงสร้างข้อมูลที่มีประสิทธิภาพมากขึ้น

สิ่งที่เป็นจริงใน C ++ ก็ใช้ได้ใน Python เราสามารถใช้แนวคิดเดียวกันและเร่งความเร็วโค้ดโดยปัจจัย 6:

def is_valid(board, empty_value='.', b_size=3):
    size = b_size * b_size
    seen = [[(size * [False]) for _ in range(size)] for _ in range(3)]
    for row in range(size):
        for col in range(size):
            if (value := board[row][col]) != empty_value:
                block = col // b_size + b_size * (row // b_size)
                dim = 0
                value = int(value) - 1
                for seen_id in [row, col, block]:
                    if seen[dim][seen_id][value]:
                        return False
                    seen[dim][seen_id][value] = True
                    dim += 1
    return True
6 Carcigenicate Nov 18 2020 at 22:13

ไมเนอร์ (และ Python) แต่โดยส่วนตัวแล้วฉันพบว่าสิ่งนี้สับสนเล็กน้อย:

if (value := board[row][col]) == empty_value:
    continue
r = f'0{row}{value}'
c = f'1{col}{value}'
b = f'2{row // b_size}{col // b_size}{value}'

คุณกำลังใช้นิพจน์การกำหนดเพื่อกำหนดค่า แต่จะใช้ในกรณีเท็จเท่านั้น ฉันคิดว่าสิ่งนี้จะสะอาดกว่ามากโดยใช้คำสั่งมอบหมายแบบธรรมดา:

value = board[row][col]
if value == empty_value:
    continue
r = f'0{row}{value}'
c = f'1{col}{value}'
b = f'2{row // b_size}{col // b_size}{value}'

ฉันไม่คิดว่าบรรทัดที่บันทึกไว้จะคุ้มค่ากับการสร้างตัวแปร

4 user673679 Nov 19 2020 at 00:04

C ++

ง่ายกว่าและอาจเร็วกว่าที่จะส่งผ่านประเภทข้อมูลธรรมดาขนาดเล็กเช่นsize_tและcharตามค่าไม่ใช่โดยการอ้างอิง ดังนั้นเราควรมี:

bool sudoku_check_update(size_t row, size_t col, char value, int block_size,
                         std::unordered_set<std::string_view> &seen)

bool sudoku_check(const std::vector<std::vector<char>> &board,
                  char empty_value = '.')

ที่สำคัญกว่า: std::string_view ไม่สามารถใช้สำหรับจัดเก็บสตริง ไม่ได้เป็นเจ้าของสตริง แต่เป็นเพียงตัวชี้และขนาด

ในการทำสิ่งนี้:

std::string_view r = "0-" + std::to_string(row) + value;

... เราสร้างชั่วคราวstd::stringแล้วกำหนดให้กับstring_view. อย่างไรก็ตามสตริงชั่วคราวจะอยู่นอกขอบเขตที่ท้ายบรรทัดนี้!

มันผ่านไปแล้ว สตริงนี้ไม่มาก มันหยุดที่จะเป็น หมดอายุแล้วไปพบผู้ผลิต นี่คือสตริงตอนปลาย มันแข็ง ชีวิตที่ไม่ได้อยู่ในความสงบ ถ้าเราไม่ตอกมันลงไปstd::string_viewมันจะเป็นการผลักดอกเดซี่ขึ้น มันพังม่านลงและเข้าร่วมกับนักร้องประสานเสียงที่มองไม่เห็น นี่คือสตริงเก่า

ในคำอื่น ๆ string_viewก็ไม่ได้กำหนดพฤติกรรมที่จะลองและการใช้งานที่ ดังนั้นr, cและbจำเป็นที่จะต้องstd::strings ตัวเอง และควรจะเป็นseenstd::unordered_set<std::string>


เรื่อง std::string_view:

std::string_viewชี้ไปที่ช่วงของอักขระในหน่วยความจำ อักขระเหล่านี้สามารถเก็บไว้ใน a std::stringในstd::arraya std::vectorหรือใน string-literal

ด้วยการใช้std::string_viewเราจะได้รับอินเทอร์เฟซเดียวกัน (การค้นหาการเปรียบเทียบการสร้างสตริงย่อย) โดยไม่คำนึงถึงสิ่งที่จัดเก็บข้อมูลพื้นฐานนั้น ดังนั้นจึงมีประโยชน์ในฐานะภาษากลางระหว่างประเภทเหล่านี้

เนื่องจากstd::string_viewไม่ได้เป็นเจ้าของอักขระจึงไม่มีการจัดสรรหน่วยความจำหรือคัดลอกตัวเอง นี้จะทำให้มันมีประโยชน์สำหรับสิ่งที่ต้องการแยกไฟล์ข้อความยาว - เราสามารถค้นหาและเปรียบเทียบในสตริงโดยไม่ต้องทำการคัดลอกที่std::stringจะทำ

ข้อเสียคือเราต้องตรวจสอบให้แน่ใจว่าอายุการใช้งานของสตริงจริงในหน่วยความจำนั้นยาวนานกว่าของstring_view.