Q1 子数组不同元素数目的平方和I
-
解题思路:
- 枚举所有子数组,用哈希表记录其中有多少个不同元素,将元素个数的平方加到答案中
-
解题代码:
class Solution {
public:
int sumCounts(vector<int>& nums) {
int n = nums.size();
long long ans = 0;
const int MOD = 1e9 + 7;
for(int i = 0; i < n; i++)
{
unordered_set<int> us;
for(int j = i; j < n; j++)
{
us.insert(nums[j]);
ans = (ans + (int)pow(us.size(), 2)) % MOD;
}
}
return ans;
}
};
Q2 使二进制字符串变美丽的最少修改次数
-
解题思路:
- 两个一组检查字符串中的字符
- 如果两个字符不同,修改次数 + 1
-
解题代码:
class Solution {
public:
int minChanges(string s) {
int ans = 0;
int n = s.size();
for(int i = 0; i < n; i += 2)
{
if(s[i] != s[i+1])
ans += 1;
}
return ans;
}
};
Q3 和为目标值的最长子序列的长度
-
解题思路:
-
一眼DP
-
先写记忆化搜索,定义dfs(i, j),表示[0, i]的子数组中,和为j的最长子序列长度
-
状态转移:
- 选,dfs(i-1, j - nums[i]) + 1,需要满足 j >= nums[i]
- 不选,dfs(i-1, j)
- 返回两者中的较大值
-
递归入口:dfs(n-1, target)
-
-
解题代码:
class Solution {
public:
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> f(n, vector<int>(target+1, -1));
function<int(int, int)> dfs = [&](int i, int j) -> int
{
if(i < 0)
return j == 0 ? 0 : INT_MIN;
if(j == 0)
return 0;
if(f[i][j] != -1)
return f[i][j];
int &ans = f[i][j];
ans = dfs(i-1, j);
if(j - nums[i] >= 0)
ans = max(ans, dfs(i-1, j - nums[i]) + 1);
return ans;
};
int ans = dfs(n-1, target);
return ans >= 0 ? ans : -1;
}
};
- 改成递推:
class Solution {
public:
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<vector<int>> f(n+1, vector<int>(target+1, INT_MIN));
f[0][0] = 0;
for(int i = 1; i <= n; i++)
{
f[i][0] = 0;
for(int j = 0; j <= target; j++)
{
f[i][j] = f[i-1][j];
if(j - nums[i-1] >= 0)
f[i][j] = max(f[i][j], f[i-1][j-nums[i-1]] + 1);
}
}
return f[n][target] > 0 ? f[n][target] : -1;
}
};
- 空间优化:
class Solution {
public:
int lengthOfLongestSubsequence(vector<int>& nums, int target) {
int n = nums.size();
vector<int> f(target+1, INT_MIN);
f[0] = 0;
for(int i = 1; i <= n; i++)
{
for(int j = target; j >= 0; j--)
{
if(j - nums[i-1] >= 0)
f[j] = max(f[j], f[j-nums[i-1]] + 1);
}
}
return f[target] > 0 ? f[target] : -1;
}
};
Q4 子数组不同元素数目的平方和II
-
相似题目:LC2262. 字符串的总引力
-
解题思路:
- lazy 线段树
- 假设一个子数组的不同计数为x,那么它的不同计数平方为x^2
- 如果子数组不同计数增加了1,那么它的不同计数的平方的增加量为(x + 1) ^ 2 - x ^ 2 = 2x + 1
- 因此需要维护区间和
- 区间求和 + 区间更新 => 线段树
-
解题代码:
class Solution {
private:
vector<long long> sum;
vector<int> todo;//懒标记
void do_(int o, int l, int r, int add)
{
sum[o] += (long long)add * (r - l + 1);
todo[o] += add;
}
long long query_and_add1(int o, int l, int r, int L, int R)
{
if(L <= l && r <= R)//该区间被查询区间完全包含
{
long long res = sum[o];
do_(o, l, r, 1);
return res;
}
int m = l + (r-l)/2;
int add = todo[o];
if(add != 0)//懒标记更新
{
do_(o * 2, l, m, add);
do_(o * 2 + 1, m+1, r, add);
todo[o] = 0;
}
long long res = 0;
if(L <= m)
res += query_and_add1(o*2, l, m, L, R);
if(m < R)
res += query_and_add1(o*2+1, m+1, r, L, R);
sum[o] = sum[o*2] + sum[o*2+1];
return res;
}
public:
int sumCounts(vector<int>& nums) {
int n = nums.size();
const int MOD = 1e9+7;
sum.resize(4 * n);
todo.resize(4 * n);
long long ans = 0, s = 0;
unordered_map<int ,int> last;
for(int i = 1; i <= n; i++)
{
int x = nums[i-1];
int j = last.count(x) ? last[x] : 0;
s += query_and_add1(1, 1, n, j+1, i) * 2 + i - j;
ans = (ans + s) % MOD;
last[x] = i;
}
return ans;
}
};