戳印序列

51 阅读8分钟

1\你想要用小写字母组成一个目标字符串 target。 

开始的时候,序列由 target.length 个 '?' 记号组成。而你有一个小写字母印章 stamp

在每个回合,你可以将印章放在序列上,并将序列中的每个字母替换为印章上的相应字母。你最多可以进行 10 * target.length  个回合。

举个例子,如果初始序列为 "?????",而你的印章 stamp 是 "abc",那么在第一回合,你可以得到 "abc??"、"?abc?"、"??abc"。(请注意,印章必须完全包含在序列的边界内才能盖下去。)

如果可以印出序列,那么返回一个数组,该数组由每个回合中被印下的最左边字母的索引组成。如果不能印出序列,就返回一个空数组。

例如,如果序列是 "ababc",印章是 "abc",那么我们就可以返回与操作 "?????" -> "abc??" -> "ababc" 相对应的答案 [0, 2]

另外,如果可以印出序列,那么需要保证可以在 10 * target.length 个回合内完成。任何超过此数字的答案将不被接受。 class Solution { public: vector movesToStamp(string stamp, string target) { size_t m = stamp.size(), n = target.size(); vector inDegree(n - m + 1, m); vector<vector> edges(n); vector seen(n); vector q; for (size_t i = 0; i < n - m + 1; ++i) { for (size_t j = 0; j < m; ++j) { if (target[i + j] == stamp[j]) { inDegree[i] -= 1; if (inDegree[i] == 0) q.emplace_back(i); } else { edges[i + j].emplace_back(i); } } } vector ans; while (!q.empty()) { int cur = q.back(); q.pop_back(); ans.emplace_back(cur); for (size_t i = 0; i < m; i++) { if (!seen[cur + i]) { seen[cur + i] = true; for (auto &&edge : edges[cur + i]) { inDegree[edge] -= 1; if (inDegree[edge] == 0) q.emplace_back(edge); } } } } if (ans.size() < n - m + 1) return {}; reverse(ans.begin(), ans.end()); return ans; } };
2\给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 class Solution { public: int quickselect(vector &nums, int l, int r, int k) { if (l == r) return nums[k]; int partition = nums[l], i = l - 1, j = r + 1; while (i < j) { do i++; while (nums[i] < partition); do j--; while (nums[j] > partition); if (i < j) swap(nums[i], nums[j]); } if (k <= j)return quickselect(nums, l, j, k); else return quickselect(nums, j + 1, r, k); }

int findKthLargest(vector<int> &nums, int k) {
    int n = nums.size();
    return quickselect(nums, 0, n - 1, n - k);
}

};

3\给定两个以 非递减顺序排列 的整数数组 nums1 和 ****nums2 ****, 以及一个整数 k ****。

定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2 ****。

请找到和最小的 k 个数对 (u1,v1) (u2,v2)  ...  (uk,vk) 。 class Solution { public: vector<vector> kSmallestPairs(vector& nums1, vector& nums2, int k) { auto cmp = [&nums1, &nums2](const pair<int, int> & a, const pair<int, int> & b) { return nums1[a.first] + nums2[a.second] > nums1[b.first] + nums2[b.second]; };

    int m = nums1.size();
    int n = nums2.size();
    vector<vector<int>> ans;   
    priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(cmp)> pq(cmp);
    for (int i = 0; i < min(k, m); i++) {
        pq.emplace(i, 0);
    }
    while (k-- > 0 && !pq.empty()) {
        auto [x, y] = pq.top(); 
        pq.pop();
        ans.emplace_back(initializer_list<int>{nums1[x], nums2[y]});
        if (y + 1 < n) {
            pq.emplace(x, y + 1);
        }
    }

    return ans;
}

};

4\给你一个整数 n ,请你找出并返回第 n 个 丑数 。

丑数 就是质因子只包含 23 和 5 的正整数。 class Solution { public: int nthUglyNumber(int n) { vector factors = {2, 3, 5}; unordered_set seen; priority_queue<long, vector, greater> heap; seen.insert(1L); heap.push(1L); int ugly = 0; for (int i = 0; i < n; i++) { long curr = heap.top(); heap.pop(); ugly = (int)curr; for (int factor : factors) { long next = curr * factor; if (!seen.count(next)) { seen.insert(next); heap.push(next); } } } return ugly; } }; 5\给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。

  • 返回 k 。 class Solution { public: int removeDuplicates(vector& nums) { int n = nums.size(); if (n == 0) { return 0; } int fast = 1, slow = 1; while (fast < n) { if (nums[fast] != nums[fast - 1]) { nums[slow] = nums[fast]; ++slow; } ++fast; } return slow; } }; 6\整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

  • 例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3][1,3,2][3,1,2][2,3,1] 。

整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

  • 例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
  • 类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
  • 而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。

给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。 class Solution { public: void nextPermutation(vector& nums) { int i = nums.size() - 2; while (i >= 0 && nums[i] >= nums[i + 1]) { i--; } if (i >= 0) { int j = nums.size() - 1; while (j >= 0 && nums[i] >= nums[j]) { j--; } swap(nums[i], nums[j]); } reverse(nums.begin() + i + 1, nums.end()); } };

7\给你两个按 非递减顺序 排列的整数数组 nums1 **和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 **到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意: 最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。
class Solution { public: void merge(vector& nums1, int m, vector& nums2, int n) { for (int i = 0; i != n; ++i) { nums1[m + i] = nums2[i]; } sort(nums1.begin(), nums1.end()); } }; 8\给定一个整数数组 nums,将数组中的元素向右轮转 k **个位置,其中 k **是非负数。 class Solution { public: void rotate(vector& nums, int k) { int n = nums.size(); vector newArr(n); for (int i = 0; i < n; ++i) { newArr[(i + k) % n] = nums[i]; } nums.assign(newArr.begin(), newArr.end()); } };

9\如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true **;否则,返回 **false 。 class Solution { public: bool isPalindrome(string s) { string sgood; for (char ch: s) { if (isalnum(ch)) { sgood += tolower(ch); } } string sgood_rev(sgood.rbegin(), sgood.rend()); return sgood == sgood_rev; } }; 10**DNA序列 由一系列核苷酸组成,缩写为 'A''C''G' 和 'T'.。

  • 例如,"ACGAATTCCG" 是一个 DNA序列 。

在研究 DNA 时,识别 DNA 中的重复序列非常有用。

给定一个表示 DNA序列 的字符串 s ,返回所有在 DNA 分子中出现不止一次的 长度为 10 的序列(子字符串)。你可以按 任意顺序 返回答案。 class Solution { const int L = 10; public: vector findRepeatedDnaSequences(string s) { vector ans; unordered_map<string, int> cnt; int n = s.length(); for (int i = 0; i <= n - L; ++i) { string sub = s.substr(i, L); if (++cnt[sub] == 2) { ans.push_back(sub); } } return ans; } }; 11\给你两个按 非递减顺序 排列的整数数组 nums1 **和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 **到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意: 最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。 class Solution { public: void merge(vector& nums1, int m, vector& nums2, int n) { for (int i = 0; i != n; ++i) { nums1[m + i] = nums2[i]; } sort(nums1.begin(), nums1.end()); } };

12\给你一个数组 nums **和一个值 val,你需要 原地 移除所有数值等于 val **的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素 class Solution { public: int removeElement(vector& nums, int val) { int n = nums.size(); int left = 0; for (int right = 0; right < n; right++) { if (nums[right] != val) { nums[left] = nums[right]; left++; } } return left; } };

13\给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。 class Solution { public: int removeDuplicates(vector& nums) { int n = nums.size(); if (n == 0) { return 0; } int fast = 1, slow = 1; while (fast < n) { if (nums[fast] != nums[fast - 1]) { nums[slow] = nums[fast]; ++slow; } ++fast; } return slow; } };