[Solutions to LeetCode Algorithm Series] Questions 26~30

LeetCode 26. Remove duplicates in ordered array (simple)

[Title description]

You are given an array arranged in ascending ordernums . Please delete the repeated elements in place so that each element appears only once and return the new length of the array after deletion. The relative order of elements should remain consistent . Then return numsthe number of unique elements in .

Considering numsthat the number of unique elements of is k, you need to do the following to ensure that your solution can be passed:

  • Change the array numsso that numsthe first kelements of contain unique elements, in the order in which they originally numsappeared in . numsThe size of the remaining elements of numsis not important.
  • Return k.

【Example 1】

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

【Example 2】

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

【hint】

1 ≤ n u m s . l e n g t h ≤ 3 ∗ 1 0 4 1\le nums.length\le 3 * 10^4 1nums.length3104
− 1 0 4 ≤ nums [ i ] ≤ 1 0 4 -10^4\the nums[i]\the 10^4104nums[i]104Already sorted
numsinascending order

【analyze】


This question uniquecan be solved in one line using the function. This function can move the repeated elements of the array to the end and return the next iterator of the last element of the array that does not repeat. For example, the original array is, after processing, it becomes, and nums = { 1, 1, 2, 2, 2, 3 }the uniquereturn { 1, 2, 3, _, _, _ }value 3is The next iterator, minus nums.begin()is the number of unique elements in the array.

We can also implement it ourselves, using two pointers, one of which is used to traverse each number. When the traversed number is different from the previous one, the number is stored in the location pointed by the other pointer, while the other pointer goes backwards. Move one position.


【Code】

[ uniqueFunction implementation]

class Solution {
    
    
public:
    int removeDuplicates(vector<int>& nums) {
    
    
        return unique(nums.begin(), nums.end()) - nums.begin();
    }
};

[Manual implementation]

class Solution {
    
    
public:
    int removeDuplicates(vector<int>& nums) {
    
    
        int k = 0;
        for (int i = 0; i < nums.size(); i++)
            if (!i || nums[i] != nums[i - 1]) nums[k++] = nums[i];
        return k;
    }
};

LeetCode 27. Remove elements (simple)

[Title description]

Given an array numsand a value val, you need to remove all valelements whose value is equal to and return the new length of the removed array.
Don't use extra array space, you have to use only O ( 1 ) O(1)O ( 1 ) extra space andin place.
The order of elements can be changed. You don't need to consider elements in the array beyond the new length.

【Example 1】

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

【Example 2】

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

【hint】

0 ≤ n u m s . l e n g t h ≤ 100 0\le nums.length\le 100 0nums.length100
0 ≤ nums [ i ] ≤ 50 0\le nums[i]\le 500nums[i]50
0 ≤ val ≤ 100 0\val\le 1000val100

【analyze】


Similar to the previous question, this question can also use removethe function to move all values ​​to the end of the array, and return the next iterator of the last element of the array after deleting the number. Subtracting is the nums.begin()length of the array after deleting the number.

We can also use two pointers, one of which iterates through all elements, and when the element is not equal to , valsave it to the location pointed by the other pointer, while the other pointer moves backward one bit.


【Code】

[ removeFunction implementation]

class Solution {
    
    
public:
    int removeElement(vector<int>& nums, int val) {
    
    
        return remove(nums.begin(), nums.end(), val) - nums.begin();
    }
};

[Manual implementation]

class Solution {
    
    
public:
    int removeElement(vector<int>& nums, int val) {
    
    
        int k = 0;
        for (int i = 0; i < nums.size(); i++)
            if (nums[i] != val) nums[k++] = nums[i];
        return k;
    }
};

LeetCode 28. Find the subscript of the first match in a string (easy)

[Title description]

Given two strings haystackand needle, please find the index of the first matching item of string haystackin string (the index starts from 0). needleIf needleis not haystackpart of , it is returned -1.

【Example 1】

输入:haystack = "sadbutsad", needle = "sad"
输出:0
解释:"sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。

【Example 2】

输入:haystack = "leetcode", needle = "leeto"
输出:-1
解释:"leeto" 没有在 "leetcode" 中出现,所以返回 -1 。

【hint】

1 ≤ haystack . length , needle length ≤ 1 0 4 1\le haystack.length, needle.length\le 10^41haystack.length,needle.length104
haystack andneedleconsist only of lowercase English characters

【analyze】


You can directly use findthe function to find the first occurrence of the substring, and return it if it does not match -1.

Of course, our focus is to talk about the KMP algorithm. Assume Sthat is a template string (that is, a longer string), Pis a pattern string (that is, a shorter string), the most important array in KMP is next[i], represents P[1 ~ i]the maximum length of all equal prefixes and suffixes (not considering the entire character string as prefix and suffix). The KMP algorithm flow is shown in the figure below:

Insert image description here

  1. Assume that the current Spointer iiThe character pointed to by i is mismatched, andPthe corresponding pointer isj + 1 j+1j+1 , at this point we knowP[1 ~ j] == S[3 ~ i - 1];
  2. Pin jjThe longest length of the same suffix at jP[1 ~ 3] == P[j - 2 ~ j] is 3 (blue mark), explain ;
  3. According to P[j - 2 ~ j]already S[i - 3 ~ i - 1]matched with , we can get that P[1 ~ 3]also S[i - 3 ~ i - 1]matches with ;
  4. So we will Pmove backward, let j = next[j], at this time j = 3 j=3j=3
  5. Continue to judge j + 1 j+1j+1 Right or wrongiii matches.

【Code】

[ findFunction implementation]

class Solution {
    
    
public:
    int strStr(string haystack, string needle) {
    
    
        return haystack.find(needle);
    }
};

[Manual implementation]

class Solution {
    
    
public:
    int strStr(string s, string p) {
    
    
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;  // 调整为下标从1开始
        vector<int> next(m + 1);
        for (int i = 2, j = 0; i <= m; i++)  // next[1]=0
        {
    
    
            while (j && p[i] != p[j + 1]) j = next[j];
            if (p[i] == p[j + 1]) j++;
            next[i] = j;
        }
        for (int i = 1, j = 0; i <= n; i++)
        {
    
    
            while (j && s[i] != p[j + 1]) j = next[j];
            if (s[i] == p[j + 1]) j++;
            if (j == m) return i - m;  // 由于下标从1开始因此起始位置为i-m
        }
        return -1;
    }
};

LeetCode 29. Dividing Two Numbers (Medium)

[Title description]

You are given two integers, the dividend dividendand the divisor divisor. To divide two numbers, multiplication, division and remainder operations are not required.
Integer division should be truncated toward zero, that is, truncate the fractional part. For example, 8.345will be truncated to 8, -2.7335will be truncated to -2.
Returns the quotient obtained dividendby dividing the dividend by the divisor . Note: Assume that our environment can only store 32-bit signed integers , whose value range is [ − 2 31 , 2 31 − 1 ] [-2^{31}, 2^{31} - 1]divisor
[231,2311 ] . In this question, if the quotientis strictly greater than 2 31 − 1 2^{31} - 12311 , then return2 31 − 1 2^{31} - 12311 ; if the quotientis strictly less than − 2 31 -2^{31}231 , then return− 2 31 -2^{31}231

【Example 1】

输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = 3.33333.. ,向零截断后得到 3 。

【Example 2】

输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = -2.33333.. ,向零截断后得到 -2 。

【hint】

− 2 31 ≤ d i v i d e n d , d i v i s o r ≤ 2 31 − 1 -2^{31}\le dividend, divisor\le 2^{31} - 1 231dividend,divisor2311
divisor ≠ 0 divisor\ne 0divisor=0

【analyze】


Assume the dividend is xxx , the divisor isyyy , the first method that can be thought of is to usexxxyyy until it can no longer be reduced, but this approach will time out.

Assume xy = k \frac {x}{y}=kyx=k , we willkkk is expressed in binary, assuming it is(110010) 2 (110010)_2(110010)2, that is, 2 1 + 2 4 + 2 5 2^1+2^4+2^521+24+25,即 x = 2 1 y + 2 4 y + 2 5 y x=2^1y+2^4y+2^5y x=21 y+24y+25y ._ Then we can preprocess2 0 y , 2 1 y , … , 2 31 y 2^0y,2^1y,\dots ,2^{31}y20 yearsold21 the_,231 y, and then enumerate from large to small, ifx ≥ 2 iyx\ge 2^iyx2i y, thenxxx minus2 iy 2^iy2i yand add2 i 2^i2i

For the judgment of sign, we can first judge the sign of the quotient, then convert the two numbers into positive numbers for calculation, and finally change the sign of the quotient.


【Code】

class Solution {
    
    
public:
    int divide(int x, int y) {
    
    
        vector<long long> exp;  // 表示2^i * y
        bool minus = (x > 0 && y < 0 || x < 0 && y > 0) ? true : false;  // 商是否为负
        long long a = abs((long long)x), b = abs((long long)y);
        for (long long i = b; i <= a; i += i) exp.push_back(i);  // 预处理
        long long res = 0;
        for (int i = exp.size() - 1; i >= 0; i--)  // 从大到小枚举
            if (a >= exp[i]) a -= exp[i], res += 1ll << i;  // 记得加long long,左移可能会越界
        if (minus) res = -res;
        if (res > INT_MAX) return INT_MAX;
        return res;
    }
};

LeetCode 30. Concatenate substrings of all words (difficult)

[Title description]

Given a string sand an array of strings words. wordsAll strings in are of the same length . The concatenated substring
s in refers to a substring containing all the strings in , arranged and connected in any order . For example, if , then , , , , and are all concatenated substrings. It is not a concatenation of substrings, because it is not a connection of any permutation. Returns the starting index of all concatenated substrings in . You can return answers in any order .words
words = ["ab","cd","ef"]"abcdef""abefcd""cdabef""cdefab""efabcd""efcdab""acdbef"words
s

【Example 1】

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

【Example 2】

输入:s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"]
输出:[]
解释:因为 words.length == 4 并且 words[i].length == 4,所以串联子串的长度必须为 16。
s 中没有子串长度为 16 并且等于 words 的任何顺序排列的连接。
所以我们返回一个空数组。

【Example 3】

输入:s = "barfoofoobarthefoobarman", words = ["bar","foo","the"]
输出:[6,9,12]
解释:因为 words.length == 3 并且 words[i].length == 3,所以串联子串的长度必须为 9。
子串 "foobarthe" 开始位置是 6。它是 words 中以 ["foo","bar","the"] 顺序排列的连接。
子串 "barthefoo" 开始位置是 9。它是 words 中以 ["bar","the","foo"] 顺序排列的连接。
子串 "thefoobar" 开始位置是 12。它是 words 中以 ["the","foo","bar"] 顺序排列的连接。

【hint】

1 ≤ s length ≤ 1 0 4 1\and s.length\and 10^41s.length104
1 ≤ words . length ≤ 5000 1\le words.length\le 50001words.length5000
1 ≤ words[i] length ≤ 30 1\le words[i].length\le 301words[i].length30
words[i] andsare composed of lowercase English letters

【analyze】


According to the amount of data in this question, the time complexity of direct brute force solution is particularly high, so some optimizations need to be considered. Let’s remember nn firstn s . s i z e ( ) s.size() s.size() m m m w o r d s . s i z e ( ) words.size() words.size() w w w w o r d s [ i ] . s i z e ( ) words[i].size() words[i].size()

Enum ssAll lengths in s arem ∗ wm*wmFor the substring of w , we divide it into several groups according to the starting position of the substring. For example, the starting position of each substring in the first group is:0 , w , 2 w , … , ( m − 1 ) w 0, w,2w,\dots ,(m-1)w0,w,2w , _,(m1 ) w , the starting position of each substring in the second group is:1 , w + 1 , 2 w + 1 , … , ( m − 1 ) w + 1 1,w+1,2w+1,\ dots ,(m-1)w+11,w+1,2w _+1,,(m1)w+1 , and so on, so that the words divided into the same group are regular and not randomly divided. For the first group, we enumerate all the lengthwwThe word substring of wbar/foo/the/foo/bar/man , for example, for the first example, the enumeration order of the first group is .

For each group we need to find the consecutive mmm intervals, the words in each interval exist inwords, so we can use a hash tablewindow_cntto maintain a length ofmmmmin sliding window of mThe number of m words, and then judgethe mmWhether m words allwordscorrespond to one-to-one in , but we cannot judge violently every time, so we can use tocntrepresent the number of words in the hash tablewords. At that cnt == mtimeSo how to determinecnt? We can then use a hash tablewords_cntto representwordsthe number of each word in . When a new word comes to the windowt,window_cnt[t]it will be increased by one. window_cnt[t] <= words_cnt[t]Whentinwordsand the number of times it has appeared in the window has not exceeded the number of timesit has appearedwordsinttimes), you cancntincrease by one; when a word is removed from the windowt,window_cnt[t]it will be decreased by one, and window_cnt[t] < words_cnt[t]whentinwordsand the number in the current window iswordsless than in ), you cancntdecrease by one.

For the first example, the solution process for the first group is as follows:

  • [bar], the length of the sliding window does not exceed mmm , add elements directly;
  • [bar, foo], the length of the sliding window does not exceed mmm , add elements directly. At this time, the content in the window corresponds towordsone-to-one, so record the answer;
  • [foo, the], the sliding window length exceeds mmm , first delete the first element and then add it. At this time, the content in the window doeswordsnot correspond to;
  • [the, foo], same as above;
  • [foo, bar], at this time, the content in the window wordscorresponds to one-to-one, and the answer is recorded;
  • [bar, man], the content in the window wordsdoes not correspond to at this time.

【Code】

class Solution {
    
    
public:
    vector<int> findSubstring(string s, vector<string>& words) {
    
    
        vector<int> res;
        int n = s.size(), m = words.size(), w = words[0].size();
        unordered_map<string, int> word_cnt;  // 记录words中每个单词的数量
        for (auto t: words) word_cnt[t]++;
        for (int i = 0; i < w; i++)
        {
    
    
            unordered_map<string, int> window_cnt;  // 滑动窗口中每个单词的数量
            int cnt = 0;  // 滑动窗口中为words中的单词的数量
            for (int j = i; j <= n - w; j += w)
            {
    
    
                if (j >= i + m * w)  // 超过滑动窗口的长度
                {
    
    
                    string t = s.substr(j - m * w, w);
                    window_cnt[t]--;
                    if (window_cnt[t] < word_cnt[t]) cnt--;  // words中不存在的单词数量为0
                }
                string t = s.substr(j, w);
                window_cnt[t]++;
                if (window_cnt[t] <= word_cnt[t]) cnt++;  // 如果某个单词数量超过了也是错的
                if (cnt == m) res.push_back(j - (m - 1) * w);
            }
        }
        return res;
    }
};

Guess you like

Origin blog.csdn.net/m0_51755720/article/details/132581666