Q1 找出峰值
-
解题思路:
- 一次遍历,依次判断元素是否同时大于其左侧和右侧元素,如果是,则将其下标加到答案数组中
-
解题代码:
class Solution {
public:
vector<int> findPeaks(vector<int>& mountain) {
vector<int> ans;
int n = mountain.size();
for(int i = 1; i < n-1; i++)
{
if(mountain[i] > mountain[i-1] && mountain[i] > mountain[i+1])
ans.push_back(i);
}
return ans;
}
};
Q2 需要添加的硬币的最小数量
-
解题思路:
-
假设当前可以表示[0, s-1]范围中的数,新来了一个数x,可以新表示的范围是[x, s+x-1]
- 如果x <= s,两个区间可以合并成[0, s+x-1]
- 如果x > s,会存在[s, x-1]范围内的数无法被表示,需要添加s到数组中
-
时间复杂度:O(nlogn + log target)
-
-
解题代码:
class Solution {
public:
int minimumAddedCoins(vector<int>& coins, int target) {
int n = coins.size();
int idx = 0;
int s = 1;
int ans = 0;
sort(coins.begin(), coins.end());
while(s <= target)
{
if(idx < n && coins[idx] <= s)
{
s += coins[idx];
idx += 1;
}
else
{
s += s;
ans += 1;
}
}
return ans;
}
};
Q3 统计完全字符串
- 题目链接
- 解题思路:
- 相邻字符在字母表中的顺序至多相差2,根据这一条件将原字符串分割成一块一块的
- 对每一块进行处理:
- 枚举子串中出现的字符个数m,这样子串的长度就是mk
- 在每一块子串上检查长度为mk的子串,是否满足其中每个字符的出现次数都是k,如果是则答案+1(滑动窗口)
- 时间复杂度:O(26 * 26 * n)
- 解题代码:
class Solution {
public:
int countCompleteSubstrings(string word, int k) {
auto check = [&](vector<int>& cnt) -> bool
{
for(int i = 0; i < 26; i++)
{
if(cnt[i] != 0 && cnt[i] != k)
return false;
}
return true;
};
auto func = [&](int start, int end) -> int
{
int res = 0;
for(int i = 1; i <= 26; i++)
{
int size = i * k;
if(size > end - start + 1)
break;
vector<int> cnt(26, 0);
int left = start, right = start;
while(right < left + size)
{
cnt[word[right] - 'a'] += 1;
right += 1;
}
if(check(cnt))
res += 1;
while(right <= end)
{
cnt[word[right] - 'a'] += 1;
right += 1;
cnt[word[left] - 'a'] -= 1;
left += 1;
if(check(cnt))
res += 1;
}
}
return res;
};
int n = word.size();
int ans = 0;
for(int i = 0; i < n; i++)
{
int j = i + 1;
while(j < n && abs(int(word[j]) - int(word[j-1])) <= 2)
j += 1;
ans += func(i, j-1);
i = j-1;
}
return ans;
}
};
- 优化时间复杂度,对出现次数再进行一次哈希,判断出现k次的字符是否有m个,时间复杂度可以优化到O(26n)
- 解题代码:
class Solution {
public:
int countCompleteSubstrings(string word, int k) {
auto func = [&](int start, int end) -> int
{
int res = 0;
for(int i = 1; i <= 26; i++)
{
int size = i * k;
if(size > end - start + 1)
break;
vector<int> cnt(26, 0);
unordered_map<int, int> um;
int left = start, right = start;
while(right < left + size)
{
um[cnt[word[right] - 'a']] -= 1;
cnt[word[right] - 'a'] += 1;
um[cnt[word[right] - 'a']] += 1;
right += 1;
}
if(um[k] == i)
res += 1;
while(right <= end)
{
um[cnt[word[right] - 'a']] -= 1;
cnt[word[right] - 'a'] += 1;
um[cnt[word[right] - 'a']] += 1;
right += 1;
um[cnt[word[left] - 'a']] -= 1;
cnt[word[left] - 'a'] -= 1;
um[cnt[word[left] - 'a']] += 1;
left += 1;
if(um[k] == i)
res += 1;
}
}
return res;
};
int n = word.size();
int ans = 0;
for(int i = 0; i < n; i++)
{
int j = i + 1;
while(j < n && abs(int(word[j]) - int(word[j-1])) <= 2)
j += 1;
ans += func(i, j-1);
i = j-1;
}
return ans;
}
};
Q4 统计感冒序列的数目
-
解题思路:
- 对于每一段[l, r],感染方案数是 2^(m-1),其中m = r - l -1;
- 不同段之间是完全独立的
- 设有m1, m2, m3三段,其和为s
- 那么方案数为:C(s, m1) * C(s-m1, m2) * C(s-m1-m2, m3) * 2^(m1+m2+m3-3)
-
解题代码:
const int MX = 100000;
const int MOD = 1e9+7;
long long fac[MX], inv_fac[MX];
long long myPow(long long a, int n)
{
if(n == 0)
return 1;
if(n & 1)
return (a * myPow(a, n-1)) % MOD;
long long temp = myPow(a, n/2);
return temp * temp % MOD;
}
auto init = [] {
fac[0] = 1;
for(int i = 1; i < MX; i++)
fac[i] = fac[i-1] * i % MOD;
inv_fac[MX-1] = myPow(fac[MX-1], MOD-2);
for(int i = MX-1; i > 0; i--)
inv_fac[i-1] = inv_fac[i] * i % MOD;
return 0;
}();
long long comb(int n, int k)
{
return fac[n] * inv_fac[k] % MOD * inv_fac[n-k] % MOD;
}
class Solution {
public:
int numberOfSequence(int n, vector<int>& sick) {
int m = sick.size();
int total = n - m;
long long ans = comb(total, sick[0]) * comb(total - sick[0], n - sick.back() - 1) % MOD;
total -= sick[0] + n - sick.back() - 1;
int e = 0;
for(int i = 0; i < m-1; i++)
{
int k = sick[i+1] - sick[i] - 1;
if(k != 0)
{
e += k-1;
ans = (ans * comb(total, k)) % MOD;
total -= k;
}
}
return ans * myPow(2, e) % MOD;
}
};