thảo luận [Học Tập] Topic thuật toán

  • Người tạo chủ đề Người tạo chủ đề unknowpc90
  • Ngày bắt đầu Ngày bắt đầu
Xem tệp đính kèm 697828
Đang học LCS, topic có bài này, áp dụng như nào nhỉ,
Cái này t làm lâu rồi, quên mất chi tiết. Chỉ nhớ ý tưởng là tại mỗi vị trí trong cái bảng, ngoài việc lưu giá trị của LCS hiện thì còn lưu thêm một value để chỉ ra LCS hiện tại đến từ đâu sao với cái LCS gần nhất. Sau khi tính là được LCS cuối cùng thì sẽ dùng value để list ra toàn bộ các chuỗi LCS.
Kiểu như này, vị trí khoanh tròn màu đỏ dùng để build ra LCS.

eKq5zewfOwNTOR3M5RYh7TAvB1520501707_kc.png
 
Cái này t làm lâu rồi, quên mất chi tiết. Chỉ nhớ ý tưởng là tại mỗi vị trí trong cái bảng, ngoài việc lưu giá trị của LCS hiện thì còn lưu thêm một value để chỉ ra LCS hiện tại đến từ đâu sao với cái LCS gần nhất. Sau khi tính là được LCS cuối cùng thì sẽ dùng value để list ra toàn bộ các chuỗi LCS.
Kiểu như này, vị trí khoanh tròn màu đỏ dùng để build ra LCS.

Xem tệp đính kèm 697858
à bài này được thảo luận ở page 20, tui đang tập tẹ học DP nên học LCS đầu tiên @@
 
c++ thì có cần học kĩ con trỏ rồi mới học ctdl&gt không các bác? Kì học c không học gì, mấy bữa rồi mở gg đọc tưởng con trỏ nó đơn giản & * thôi, giờ tìm tài liệu đọc kĩ thấy dài phết :cry:
 
Lâu không ai post gì mình share 1 bài medium leetcode, đề bài khá đơn giản: cho 1 string, tìm palindromic substring lớn nhất (palindromic string là string mà viết xuôi viết ngược như nhau).
Các thím thử sức xem làm trong bao lâu, mình để gợi ý và lời giải ở dưới.
Thím nào có lời giải hay hơn thì đóng góp luôn.
https://leetcode.com/problems/longest-palindromic-substring/
Xem tệp đính kèm 685718

Dùng quy hoạch động, cải tiến từ bài toán longest common substring.
Lời giải của mình:
- Để tìm longest palindromic substring của s, ta có thể dựa vào ý tưởng tìm longest common substring của s và chuỗi đảo của chính nó (rs), vì khi đảo lại s thì longest palindromic substring không đổi
VD: s là xxxabcdcba và chuỗi đảo rsabcdcbaxxx, khi cho vào thuật toán lc substr sẽ tìm được abcdcba.

- Tuy nhiên longest common substring chưa chắc đã là palindromic string
VD: s là xxxabcdxxxdcba thì rs là abcdxxxdcbaxxx, khi tìm lcsubstr của 2 chuỗi sẽ ra dcba hoặc abcd, sai so với yêu cầu.
Vì thế khi tìm được 1 chuỗi con chung dài nhất, sẽ check xem chuỗi có phải là palindromic string không.

Thuật toán:

1. Áp dụng lcsubstr, tìm 1 chuỗi con chung của 2 s và rs.
2. Khi được 1 chuỗi con chung:
2.1 Nếu độ dài chuỗi <= độ dài longest palindromic substring hiện tại, quay lại bước 1.
2.2 Check xem chuỗi có phải palindromic string không, nếu không quay lại bước 1.
2.3 Update độ dài lớn nhất và vị trí của chuỗi trong s hoặc rs.
3. Sử dụng vị trí và độ dài tìm được để trả về chuỗi kết quả.

Code hơi messy chút vì viết cho chạy luôn, tối ưu thì sẽ chạy nhanh hơn và đỡ tốn memory hơn.
C++:
class Solution {
public:
    string longestPalindrome(string s) {
        string rs = std::string(s);
        std::reverse(rs.begin(),rs.end());
   
        int n = s.size();
   
        //if (n <= 1) return s;
   
        int arr[n+2][n+2];
        memset(arr, 0, sizeof arr);
        int maxVal = 0, maxX = 0, maxY = 0;
   
        for (int i = 1; i <= n+1; ++i) {
            for (int j = 1; j <= n+1; ++j) {
                if (i < n+1 && j < n+1 && s[i-1] == rs[j-1]) {
                    arr[i][j] = arr[i-1][j-1] + 1;
                }
                else {
                    if (arr[i-1][j-1] > maxVal) {
                        int size = arr[i-1][j-1];
                        bool check = true;
                        for (int pl = 0; pl <= size/2; ++pl) {
                            if (s[i-2-pl] != s[i-1-size+pl]) {
                                check = false;
                                break;
                            }
                        }
                   
                        if (check) {
                            maxX = i-2;
                            maxY = j-2;
                            maxVal = arr[i-1][j-1];
                        }
                    }
                }
            }
        }
   
        string ans = s.substr(maxX - maxVal + 1,maxVal);
   
        return ans;
    }
};
tui không hiểu đoạn code này, fen giải thích tui cái, mấy tiếng cả chiều đến h đọc code mà ko ngẫm ra

C++:
for (int pl = 0; pl <= size/2; ++pl) {
                            if (s[i-2-pl] != s[i-1-size+pl]) {
                                check = false;
                                break;
                            }
                        }

clonemasteruwu bài này chỉ xét các phần tử liên tục à fen, thấy ông này chỉ cần vị trí và độ dài của subsstring, chứ thuật toán lcs là có thể là subsequence ko cần liền nhau mà nhỉ??
 
Sửa lần cuối:
tui không hiểu đoạn code này, fen giải thích tui cái, mấy tiếng cả chiều đến h đọc code mà ko ngẫm ra

C++:
for (int pl = 0; pl <= size/2; ++pl) {
                            if (s[i-2-pl] != s[i-1-size+pl]) {
                                check = false;
                                break;
                            }
                        }
Đoạn code này để kiểm tra 1 common substring có phải là palindromic string hay không.
arr[j] == 0 và arr[i-1][j-1] != 0 nghĩa là common kết thúc ở arr[i-1][j-1] và độ dài của chuỗi bằng arr[i-1][j-1], duyệt ngược lại nửa chuỗi để xem nó có phải palindromic string không thôi.

Tuy nhiên thím có thể tham khảo các cách khác, như quy hoạch động ở trên, mình thấy đơn giản và mạch lạc hơn.

Chắc thím biết bài toàn tìm longest common substring rồi, nếu không có thể tham khảo ở:
https://en.wikipedia.org/wiki/Longest_common_substring_problem
 
Đoạn code này để kiểm tra 1 common substring có phải là palindromic string hay không.
arr[j] == 0 và arr[i-1][j-1] != 0 nghĩa là common kết thúc ở arr[i-1][j-1] và độ dài của chuỗi bằng arr[i-1][j-1], duyệt ngược lại nửa chuỗi để xem nó có phải palindromic string không thôi.

Tuy nhiên thím có thể tham khảo các cách khác, như quy hoạch động ở trên, mình thấy đơn giản và mạch lạc hơn.

Chắc thím biết bài toàn tìm longest common substring rồi, nếu không có thể tham khảo ở:
https://en.wikipedia.org/wiki/Longest_common_substring_problem
nhưng mà cách thím là đang mặc định cho substring này nó liền mạch hay sao ấy, mà trong đề leetcode nó không nói kỹ phần này, đề ngắn vl
 
https://leetcode.com/problems/minimum-subsequence-in-non-increasing-order/
Để giảiđược bài này thì cần kiến thức gì mấy bác? Chỉ em với, suy nghĩ cả buổi chiều ko biết làm sao
Bài này mình nghĩ có thể dùng queue.

Đầu tiên thím tính tổng cả array để ra total_sum, sau đấy mình có cái target_sum = total_sum/2 + 1 là tổng mà subsequence cần đạt được.

Sau đấy thím dùng queue lấy từng số, nếu tổng chưa đạt được thì push, tổng vượt thì pop.

Độ phức tạp O(2n).
 
https://leetcode.com/problems/minimum-subsequence-in-non-increasing-order/
Để giảiđược bài này thì cần kiến thức gì mấy bác? Chỉ em với, suy nghĩ cả buổi chiều ko biết làm sao
Bài này cách đơn giản nhất là dùng greedy nhé. Lấy sum của nguyên dãy. Sau đó sort theo thứ tự giảm dần. Rồi lấy ra n phần tử đầu tiên > sum/2. Code như này:
C++:
class Solution {
public:
    vector<int> minSubsequence(vector<int>& nums) {
        int sum = accumulate(nums.begin(), nums.end(), 0);
        sort(nums.begin(), nums.end(), greater());
        
        int sumSub = 0;
        auto it = nums.begin();
        while (sumSub <= sum/2){
            sumSub += *it;
            it++;
        }
        return vector(nums.begin(), it);
    }
};

Runtime: 10 ms, faster than 14.73% of C++ online submissions for Minimum Subsequence in Non-Increasing Order.

Cách này chưa được tối ưu lắm. Có tgian mình sẽ nghĩ cách tốt hơn. :D
 
https://leetcode.com/problems/minimum-subsequence-in-non-increasing-order/
Để giảiđược bài này thì cần kiến thức gì mấy bác? Chỉ em với, suy nghĩ cả buổi chiều ko biết làm sao
C++:
class Solution {
public:
    vector<int> minSubsequence(vector<int>& nums) {
        sort(nums.begin(), nums.end(), greater<int>());
        vector<int>ans;
        for(int i=0;i<nums.size();i++){
            int sum1=0,sum2=0;
            for(int j=0;j<=i;j++){
                sum1+=nums[j];
            }
            for(int j=i+1;j<nums.size();j++){
                sum2+=nums[j];
            }
            if(sum1>sum2){
                for(int k=0;k<=i;k++){
                    ans.push_back(nums[k]);
                }
                break;
            }
        }
        return ans;
    }
};
c căn bản học 1 tuần là làm ngon, mấy bài easy thấy có cần thuật toán đâu (e làm được hơn 100 bài easy rồi). Cùng lắm là có đệ quy, hầu hết là cứ sort với two pointer là giải quyết gần hết, à còn phải mấy ctdl nữa
 
C++:
class Solution {
public:
    vector<int> minSubsequence(vector<int>& nums) {
        sort(nums.begin(), nums.end(), greater<int>());
        vector<int>ans;
        for(int i=0;i<nums.size();i++){
            int sum1=0,sum2=0;
            for(int j=0;j<=i;j++){
                sum1+=nums[j];
            }
            for(int j=i+1;j<nums.size();j++){
                sum2+=nums[j];
            }
            if(sum1>sum2){
                for(int k=0;k<=i;k++){
                    ans.push_back(nums[k]);
                }
                break;
            }
        }
        return ans;
    }
};
c căn bản học 1 tuần là làm ngon, mấy bài easy thấy có cần thuật toán đâu (e làm được hơn 100 bài easy rồi). Cùng lắm là có đệ quy, hầu hết là cứ sort với two pointer là giải quyết gần hết, à còn phải mấy ctdl nữa
Xử lý phức tạp vậy thím, :D
Coi post của mình nhé:
https://voz.vn/t/hoc-tap-topic-thuat-toan.182659/page-25#post-11432537
 

Thống kê chủ đề

Ngày tạo
unknowpc90,
Người trả lời cuối
Spaghetti Code,
Trả lời
1.460
Lượt xem
154.147
Quay lại
Lên đầu trang