1566. 重复至少 K 次且长度为 M 的模式 #模拟
题意
给定正整数数组 arr,请你找出一个长度为 m 且在数组中至少重复 k 次的模式。
模式 是由一个或多个值组成的子数组(连续的子序列),连续 重复多次但 不重叠 。 模式由其长度和重复次数定义。如果数组中存在至少重复 k 次且长度为 m 的模式,则返回 true ,否则返回 false 。
分析
题目初看有点麻烦,但实际上隔着特定周期(即重复长度)去检查相应字符是否相等,模拟一下题意即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution { public: bool containsPattern(vector<int>& arr, int m, int k) { int cnt = 0; int len = arr.size(); int tmp = 0; for (int i = 0; i < len; i++){ if(tmp == m){ cnt++; tmp = 0; } if(i + m >= len) break; if(arr[i] == arr[i + m]) tmp++; else{ if(cnt >= k - 1) return true; cnt = 0; tmp = 0; } } return cnt >= k - 1; } };
|
1567. 乘积为正数的最长子数组长度 #贪心
题意
给定整数数组 nums ,请你求出乘积为正数的最长子数组的长度。其中,一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。
分析
我的版本(代码略长)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Solution { public: int getMaxLen(vector<int>& nums) { int len = nums.size(); int cnt = 0, mymax = -0x3f3f3f3f; bool last = false; for (int i = 0; i < len; i++){ cnt++; if(nums[i] == 0){ cnt = 0; last = false; } else if(nums[i] < 0){ if(last == false) last = true; else last = false; } if(last == false) mymax = max(mymax, cnt); } cnt = 0; last = false; for(int i = len - 1; i >= 0; i--){ cnt++; if(nums[i] == 0){ cnt = 0; last = false; } else if(nums[i] < 0){ if(last == false) last = true; else last = false; } if(last == false) mymax = max(mymax, cnt); } return (mymax == -0x3f3f3f3f) ? cnt - 1 : mymax; } };
|
@xu012的队列版本,易写,不过复杂度有点高
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Solution { public: int getMaxLen(vector<int>& nums) { int len = nums.size(); int last = -1, ans = -1; queue<int> myque; for (int i = 0; i < len; i++){ if(nums[i] == 0){ last = i; queue<int> tmp; myque.swap(tmp); } else if(nums[i] < 0) myque.push(i); if(myque.size() % 2 == 0) ans = max(ans, i - last); else ans = max(ans, i - myque.front()); } return ans; } };
|
1569. 将子数组重新排序得到同一个二叉查找树的方案数 #分治 #组合数
题意
给定数组 nums表示 1 到 n 的一个排列。按照元素在nums中的顺序依次插入一个初始为空的二叉查找树。请你统计将 nums 重新排序后,统计满足如下条件的方案数:重排后得到的二叉查找树与 nums 原本数字顺序得到的二叉查找树相同,将结果对 10^9 + 7 取余数。
样例

分析
显然第一个插入的元素必为根节点,设为rt,通过rt的大小,我们就能决定数组后面的元素如何划分至左右子树。我们设lo表示小于rt的元素构成的集合,hi表示大于rt元素集合,显然lo、hi分别代表左、右子树节点集。
观察到,无论我先从lo集合中取元素插入rt左子树,还是我先从hi集合取元素插入右子树,树的拓扑结构并不影响,简单来说,两个大集合谁先谁后插入搜索树中,都不会发生相互影响。
那么,集合内部的元素插入顺序是否影响呢?我们回想第一句话,第一个插入元素必为根节点。改变了第一个插入的元素,就会使得新树的结构不再于给定的原树结构相同了。
我们只要保证左子树的元素内部相对顺序,以及右子树的元素内部相对顺序,调整两种集合的插入顺序,能够获得新插入序列,但不影响原树的结构。
如何统计?从n个元素中将k个元素(均属于某一集合)挑选出来,同时保证这k个元素相对顺序相同(需要去序),共有Cnk(或Clo.size+hi.sizelo.size)个组合。当然我们只是将两个大集合的组合统计了,大集合内部同样有如此的统计方式,由乘法原理得到f(n)=f(lo)×f(hi)×Clo.size+hi.sizelo.size
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| typedef long long ll; ll c[1010][1010]; class Solution { private: const int MOD = 1e9+7; public: void Calculate(int len){ for(int i = 1; i <= len; i++){ c[i][0] = c[i][i] = 1; for(int j = 1; j < i; j++) c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD; } } int dfs(vector<int>& arr){ vector<int> lo, hi; if(arr.size() <= 1) return 1; for (int i = 1; i < (int)arr.size(); i++){ if(arr[i] < arr[0]) lo.push_back(arr[i]); else hi.push_back(arr[i]); } ll cur = c[lo.size() + hi.size()][lo.size()]; cur = (cur * dfs(lo)) % MOD; cur = (cur * dfs(hi)) % MOD; return cur % MOD; } int numOfWays(vector<int>& nums) { Calculate(nums.size()); return (dfs(nums) - 1 + MOD) % MOD; } };
|
1568. 使陆地分离的最少天数 #Tarjan求割点
题意
给你一个由若干 0 和 1 组成的二维网格 grid ,其中 0 表示水,而 1 表示陆地。岛屿由水平方向或竖直方向上相邻的 1 (陆地)连接形成。
如果 恰好只有一座岛屿 ,则认为陆地是 连通的 ;否则,陆地就是 分离的 。一天内,可以将任何单个陆地单元(1)更改为水单元(0)。现要求使陆地分离的最少天数。

分析
Tarjan割点的算法我还没学,暂时先咕
其实该题有个漏洞,所有岛屿都有小边角,只要将小边角切出一块即可,也就说最多天数为2。