leetcode problem solving hash

Posted by kustomjs on Mon, 28 Feb 2022 14:07:24 +0100

4, Hash

1. 242 effective letter ectopic words

Given two strings s and t, write a function to judge whether t is an alphabetic ectopic word of s.

Note: if each character in S and t appears the same number of times, s and T are called alphabetic words.

Source: LeetCode
Link: https://leetcode-cn.com/problems/valid-anagram
The copyright belongs to Lingkou network. For commercial reprint, please contact the official authorization, and for non-commercial reprint, please indicate the source.

class Solution {
public:
    bool isAnagram(string s, string t) {
        vector<int> vec1(26,0);
        vector<int> vec2(26,0);
        for(int i = 0 ; i < s.size() ; i++){
            vec1[s[i] - 'a']++;
        }
        for(int i = 0 ; i < t.size() ; i++){
            vec2[t[i] - 'a']++;
        }

        for(int i = 0 ; i < 26 ; i++){
            if(vec1[i] != vec2[i])
                return false;
        }
        return true;

    }
};

This problem obviously uses the idea of hash, but the idea of hash does not have to use unordered_ When the data structure of map is simple, such as 26 English letters, we can directly use the hash table of array, because the array itself is also a hash data structure. It is convenient to use vector to traverse the string respectively, and then compare whether the number of letters in the hash table is the same.

2. 383. Ransom letter

Give you two strings: ransomNote and magazine. Judge whether ransomNote can be composed of characters in magazine.

If yes, return true; Otherwise, false is returned.

Each character in magazine can only be used once in ransomNote.

Source: LeetCode
Link: https://leetcode-cn.com/problems/ransom-note
The copyright belongs to Lingkou network. For commercial reprint, please contact the official authorization, and for non-commercial reprint, please indicate the source.

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {

        vector<int> vec(26,0);

        for(int i = 0 ; i < magazine.size() ; i++){
            vec[magazine[i] - 'a']++;
        }

        for(int i = 0 ; i < ransomNote.size() ; i++){
            vec[ransomNote[i] - 'a']--;
        }

        for(int i = 0 ; i < 26 ; i++)
            if(vec[i] < 0)
                return false;

        return true;
    }
};

This question is basically the same as the one above

3. 438. Find all letter words in the string

Given two strings S and p, find the substrings of all ectopic words of p in s, and return the starting indexes of these substrings. The order in which answers are output is not considered.

Ectopic words refer to strings (including the same string) formed by the rearrangement of the same letters.

Source: LeetCode
Link: https://leetcode-cn.com/problems/find-all-anagrams-in-a-string
The copyright belongs to Lingkou network. For commercial reprint, please contact the official authorization, and for non-commercial reprint, please indicate the source.

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {

        if(p.size() > s.size())
            return  vector<int>{};

        vector<int> vec1(26,0);
        vector<int> vec2(26,0);

        vector<int> result;

        // Calculate the hash table of p
        for(int i = 0 ; i < p.size() ; i++){
            vec1[p[i] - 'a']++;
        }
        // vec2 is used to record the hash table of s string
        for(int i = 0 ; i < p.size() ; i++){
            vec2[s[i] - 'a']++;
        }
        for(int i = p.size() - 1 ; i < s.size() ; i++){
            if(vec1 == vec2){
                result.push_back(i - p.size() + 1);
            }
            if(i != s.size() - 1){
                vec2[s[i - p.size() + 1] - 'a']--;
                vec2[s[i+1] - 'a']++;
            }

        }

        return result;
    }
};

It is also the topic of finding ectopic words. The main idea is to compare ectopic words with hash array, and then traverse the s string. Since the length of p is fixed, the traversal is also relatively simple. The hash array of the string with length p starting with the ith character can be compared with the hash array of p. here, it is also handled skillfully to avoid repeated calculation.

4. 349. Intersection of two arrays

Given two arrays nums1 and nums2, return their intersection. Each element in the output result must be unique. We can ignore the order of output results.

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {

        vector<int> v2(1001,0);
        vector<int> vec;

        for(int i = 0 ; i < nums2.size() ; i++){
            v2[nums2[i]] = 1;
        }

        for(int i = 0 ; i < nums1.size() ; i++){
            if(v2[nums1[i]] != 0){
                vec.push_back(nums1[i]);
                v2[nums1[i]] = 0;
            }
        }

        return vec;
    }
};

Using the idea that if you want to judge whether the element is in the set, you can use the hash method. My practice is to establish the hash data structure of array, and put the element of one of the array into the hash table. The array element is 1 or 0, indicating whether the element is included in the original array. Here, because the title gives the specific range of parameters, you can do so, If not, put it into set and use set Count is more convenient to judge. It's also the official practice of leetcode

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> set1, set2;
        for (auto& num : nums1) {
            set1.insert(num);
        }
        for (auto& num : nums2) {
            set2.insert(num);
        }
        return getIntersection(set1, set2);
    }

    vector<int> getIntersection(unordered_set<int>& set1, unordered_set<int>& set2) {
        if (set1.size() > set2.size()) {
            return getIntersection(set2, set1);
        }
        vector<int> intersection;
        for (auto& num : set1) {
            if (set2.count(num)) {
                intersection.push_back(num);
            }
        }
        return intersection;
    }
};

Author: LeetCode-Solution
 Link: https://leetcode-cn.com/problems/intersection-of-two-arrays/solution/liang-ge-shu-zu-de-jiao-ji-by-leetcode-solution/
Source: force buckle( LeetCode)
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

However, it should be noted that the problem of using arrays for hashing is that the problems limit the size of values.

This problem does not limit the size of values, so we can't use arrays as hash tables.

Moreover, if the hash value is relatively small, especially scattered and the span is very large, the use of array will cause a great waste of space.

At this point, we need to use another structure, set. About set, C + + provides the following three available data structures:

But hash problem:

Using set directly not only takes up more space than the array, but also is slower than the array. When set maps values to key s, it needs to do hash calculation.

Don't underestimate this time-consuming. In the case of large amount of data, the gap is obvious.

It is worth mentioning that the constructors of vector data structures can be easily transformed, such as the transformation from set to vector

return vector<int>(res.begin(),res.end());

5. 350. Intersection of two arrays II

Here are two integer arrays nums1 and nums2. Please return the intersection of the two arrays in the form of array. The number of occurrences of each element in the returned result should be consistent with the number of occurrences of elements in both arrays (if the number of occurrences is inconsistent, consider taking the smaller value). The order of output results can be ignored.

Source: LeetCode
Link: https://leetcode-cn.com/problems/intersection-of-two-arrays-ii
The copyright belongs to Lingkou network. For commercial reprint, please contact the official authorization, and for non-commercial reprint, please indicate the source.

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        vector<int> v2(1001,0);
        vector<int> vec;

        for(int i = 0 ; i < nums2.size() ; i++){
            v2[nums2[i]]++;
        }

        for(int i = 0 ; i < nums1.size() ; i++){
            if(v2[nums1[i]] != 0){
                vec.push_back(nums1[i]);
                v2[nums1[i]]--;
            }
        }

        return vec;
    }
};

class Solution {
public:
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {

        unordered_map<int,int> map;
        vector<int> result;

        for(int num : nums1){
            map[num]++;
        }

        for(int num : nums2){
            if(map[num] != 0){
                result.push_back(num);
                map[num]--;
            }
        }

        return result;
    }
};

This question is similar to the one above, but it needs to repeat elements, so it's not very good to simply use set. You have to use a data structure that contains both key and value, map, or directly use an array and index to represent key and the corresponding value to represent value

6. 202. Happy number

Write an algorithm to judge whether a number n is a happy number.

"Happy number" is defined as:

For a positive integer, replace the number with the sum of the squares of the numbers at each position each time.
Then repeat the process until the number becomes 1, or it may be an infinite loop, but it never becomes 1.
If the result of this process is 1, then this number is the number of happiness.
If n is a happy number, return true; If not, false is returned.

Source: LeetCode
Link: https://leetcode-cn.com/problems/happy-number
The copyright belongs to Lingkou network. For commercial reprint, please contact the official authorization, and for non-commercial reprint, please indicate the source.

class Solution {
public:
    bool isHappy(int n) {

        unordered_map<int,int> map;
        while(true){
            if(n == 1)
                return true;
            n = process(n);
            if(map[n] == 0){
                map[n]++;
            }else{
                return false;
            }
        }

    }

    int process(int n){
        int sum = 0 ;
        while(n != 0){
            sum += (n%10)*(n%10);
            n = n /10;
        }

        return sum;
    }
};

The difficulty of this question lies in the personal understanding of the meaning of the question. If there are two identical numbers during cyclic calculation, it proves that the later calculated numbers will start to cycle, because the calculation formulas are the same. I didn't understand this at first, but calculated according to the definition of cycle, so it's more troublesome. After understanding this, It is a simple program calculation, plus the hash to judge whether there are duplicate elements. "Quickly judge whether an element is in the set, and use the hash" time is faster than traversing the array, which is O(1).

It is worth mentioning here that unordered is used_ map<int,int> map; After initialization, if the value is assigned in the form of an array, the value corresponding to the key that has not yet appeared in the map is 0; In other words, if you want to judge whether there is a key value in the map, you can judge by whether the map ["key"] is 0. Moreover, after testing, as long as the word map ["key"] appears and is not initialized, the corresponding value of the key will be 0, as shown below:

7. 1. Sum of two numbers

Given an integer array nums and an integer target value target, please find the two integers with and as the target value target in the array and return their array subscripts.

You can assume that each input will correspond to only one answer. However, the same element in the array cannot appear repeatedly in the answer.

You can return answers in any order.

Source: LeetCode
Link: https://leetcode-cn.com/problems/two-sum
The copyright belongs to Lingkou network. For commercial reprint, please contact the official authorization, and for non-commercial reprint, please indicate the source.

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {

        unordered_map<int,int> map;
        map[nums[0]] = 0;
        int right;
        int left;
        for(int i = 1 ; i < nums.size() ; i++){
            if(map.count(target - nums[i]) != 0){
                right = i;
                left = map[target - nums[i]];
                break;
            }
            else{
                map.insert(pair<int,int>(nums[i],i));
            }

        }

        return vector<int>{left,right};
    }
};

This topic mainly considers using map to store hash value, because this topic is essentially to quickly find an element in the array, so use map. Since the topic requires to return the index, the value of map is used to store the value of the index, and then practice the basic usage of map.

unordered_map key cannot be repeated.

8. 454. Four number addition II

Here are four integer arrays nums1, nums2, nums3 and nums4. The array length is n. please calculate how many tuples (i, j, k, l) can satisfy:

0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

Source: LeetCode
Link: https://leetcode-cn.com/problems/4sum-ii
The copyright belongs to Lingkou network. For commercial reprint, please contact the official authorization, and for non-commercial reprint, please indicate the source.

//Violent solution timeout

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {

        int length = nums1.size();

        vector<int> vec1(length * length);
        vector<int> vec2(length * length);

        for(int i = 0 ; i < length ; i++){
            for(int j = 0 ; j < length ; j++){
                vec1[i * length + j] = nums1[i] + nums2[j];
            }
        }

        for(int i = 0 ; i < length ; i++){
            for(int j = 0 ; j < length ; j++){
                vec2[i * length + j] = nums3[i] + nums4[j];
            }
        }

        int result = 0 ;

        for(int i = 0 ; i < length * length ; i++){
            for(int j = 0 ; j < length * length ; j++)
                if(vec1[i] + vec2[j] == 0)
                    result++;
        }

        return result;
    }
};

// The method of hashing is similar to my previous idea of using the violence method, except that the value of a+b is not put in the vector, but in the map, which is also in line with the idea of fast search.

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {

unordered_map<int, int> umap; //Key: the value of a + B, value: the number of times the value of a + B appears
        // Traverse the large A and B arrays, count the sum of the two array elements and the number of occurrences, and put them into the map
        for (int a : nums1) {
            for (int b : nums2) {
                umap[a + b]++;
            }
        }
        int count = 0; // Count the number of occurrences of a+b+c+d = 0
        // After traversing the large C and large D arrays, if 0-(c+d) has appeared in the map, the value corresponding to the key in the map, that is, the number of occurrences, will be counted.
        for (int c : nums3) {
            for (int d : nums4) {
                if (umap.find(0 - (c + d)) != umap.end()) {
                    count += umap[0 - (c + d)];
                }
            }
        }
        return count;
    }
};

Put all the values of a+b into the hash table, then traverse the values of c+d, and quickly find out whether the required values appear in a+b through target, but the complexity is inevitably O(n2)

9.15. Sum of three

Is to give you an array of N integers, nums, and a target value target. Please find and return the quads [nums[a], nums[b], nums[c], nums[d]] (if the two Quad elements correspond one by one, it is considered that the two quads are repeated): 0 < = a, B, C, d < n
a. b, c and d are different from each other
nums[a] + nums[b] + nums[c] + nums[d] == target
You can return answers in any order.

// The less violent algorithm also timed out

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {

        if(nums.size() < 3)
            return vector<vector<int>>{};

        unordered_map<int,vector<int>> map;
        set<vector<int>> result;
        for(int i = 0 ; i < nums.size() ; i++)
            map[nums[i]].push_back(i);

        for(int i = 0 ; i < nums.size() ; i++ ){
            for(int j = i + 1 ; j < nums.size() ; j++ ){
                if(map.count(-(nums[i] + nums[j])) != 0){
                    for(auto value : map[-(nums[i] + nums[j])]){
                        if(value != i && value != j){
                            vector<int> temp = {nums[i],nums[j],nums[value]};
                            sort(temp.begin(),temp.end());
                            result.insert(temp);
                        }
                    }
                }
            }
        }

        return vector<vector<int>>(result.begin() , result.end());

    }
};

				vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // Find a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.size(); i++) {
            // After sorting, if the first element is greater than zero, it is impossible to combine into triples in any case. Just return the result directly
            if (nums[i] > 0) {
                return result;
            }
            // If the duplicate removal method is wrong, the case of - 1, - 1,2 will be omitted
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }
            */
            // Correct de duplication method
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (right > left) {
                // If the de duplication logic is placed here, the case of 0, 0, 0 may directly lead to right < = left, thus missing the triple of 0,0,0
                /*
                while (right > left && nums[right] == nums[right - 1]) right--;
                while (right > left && nums[left] == nums[left + 1]) left++;
                */
                if (nums[i] + nums[left] + nums[right] > 0) {
                    right--;
                } else if (nums[i] + nums[left] + nums[right] < 0) {
                    left++;
                } else {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // De duplication logic should be placed after finding a triple
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;

                    // When the answer is found, the double pointer shrinks at the same time
                    right--;
                    left++;
                }
            }

        }
        return result;

// Double pointer method
        sort(nums.begin(),nums.end());
        int left,right;
        vector<vector<int>> result{};

        for(int i = 0 ; i < nums.size() ; i++){

            if(nums[i] > 0)
                return result;

            if(nums[i] == nums[i - 1] && i > 0)
                continue;

            left = i + 1;
            right = nums.size() - 1;

            int sum = nums[i] + nums[left] + nums[right];

            while(left < right){
                if( sum < 0){
                    left++;
                }else if(sum > 0){
                    right--;
                }else{
                    result.push_back(vector<int>{i,left,right});
                    while(right > left && nums[right] == nums[right - 1])
                        right--;
                    while(right > left && nums[left] == nums[left + 1])
                        left++;
                    left++;
                    right--;
                }
            }
        }

        return result;

The idea of method 1 is as follows: the two-layer for loop can determine the values of a and B. the hash method can be used to determine whether 0-(a+b) has appeared in the array. In fact, this idea is correct

Reason for timeout: put the qualified triples into the vector and then remove the duplicate. This is very time-consuming and easy to timeout. It is also the root of the low pass rate of this problem.

Then, the return value required by the question should be specifically considered. The return value of this question is the value of the element, not the subscript, so it is necessary to manually remove the duplicate, and pay attention to how to manually remove the duplicate.

The double pointer method reduces the time complexity: O ( n 2 ) O(n^2) The solution optimization of O(n2) is O ( n ) O(n) Solution of O(n). That is, down one order of magnitude

10.18. Sum of four numbers

Give you an array of N integers, nums, and a target value, target. Please find and return the quads [nums[a], nums[b], nums[c], nums[d]] (if the two Quad elements correspond one by one, it is considered that the two quads are repeated): 0 < = a, B, C, d < n
a. b, c and d are different from each other
nums[a] + nums[b] + nums[c] + nums[d] == target
You can return answers in any order.

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());
        for(int i = 0 ; i < nums.size() ; i++){

            if(i > 0 && nums[i] == nums[i -1 ])
                continue;
            for(int j = i + 1 ; j < nums.size() ; j++){

                if(j > i + 1 && nums[j] == nums[j -1])
                    continue;

                int left = j + 1 , right = nums.size() - 1;
                while(left < right){
                    if(nums[i] + nums[j] < target - (nums[left] + nums[right])){
                        left++;
                    }else if(nums[i] + nums[j] > target - (nums[left] + nums[right])){
                        right--;
                    }else{
                        result.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});

                        while(left < right && nums[right] == nums[right - 1])
                            right--;
                        while(left < right && nums[left] == nums[left + 1])
                            left++;
                        left++;
                        right--;
                    }
                }
            }
        }

        return result;
    }
};

The sum of four numbers in this question is similar to the sum of three numbers in the above question, but it needs more cycles. Generally, the idea of double pointers is adopted. From these two questions, double pointers can reduce the time complexity to the first level. Here, especially considering that the return value is the value of the element, de duplication is a key problem. If the hash table is used for storage, So the time complexity of finding tuples and removing duplicates will make the calculation time-out, so these two questions use the idea of double pointers.

In addition, because the value of target is uncertain, the cycle cannot be ended after the minimum value is greater than 0, as the sum of three numbers. When the target is negative, even if the minimum value is greater than him, it may not meet the requirements.

Topics: Algorithm leetcode