LeetCode周赛第313场记录
2427. 公因子的数目
这道题是让找出两个整数a,b之间的公因子数目,解决思路非常简单,就是找出两个数字中较小的一个数字,逐个遍历统计符合要求的数字个数即可。思路很简单,完整代码如下:
int commonFactors(int a, int b)
{
int Min = min(a,b);
int Ans = 0;
for(int i = 1 ; i <= Min ; ++i)
if((a % i == 0) && (b % i == 0))
Ans++;
return Ans;
}
2428. 沙漏的最大总和
这道题首先给出了一个沙漏形状的定义,然后让我们找出最大沙漏的总和。
首先考虑如何计算一个沙漏,其实很简单,就是一个完整的
3
×
3
3 \times 3
3×3的矩形方块减去两个小块即可。
所以我首先写出了计算沙漏形状总和的函数:calculateSum,然后直接在整个网格grid中遍历求最大值即可,思路也很简单,完整代码如下:
// (i,j) is the left upper point
int calculateSum(vector<vector<int>>& grid, int i, int j, int m, int n)
{
int Ans = 0;
if(i + 2 < m && j + 2 < n) // legal
{
for(int k = 0 ; k < 3 ; ++k)
for(int l = 0 ; l < 3 ; ++l)
Ans += grid[i + k][j + l];
Ans -= grid[i + 1][j];
Ans -= grid[i + 1][j + 2];
}
return Ans; // 0 means not legal
}
class Solution {
public:
int maxSum(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
int Ans = 0;
for(int i = 0 ; i < m ; ++i)
for(int j = 0 ; j < n ; ++j)
Ans = max(Ans, calculateSum(grid, i, j, m, n));
return Ans;
}
};
2429. 最小 XOR
这道题开始有些难度了,一方面这个数字中的1的数量和num2保持一致,又要要求它和num1异或的结果最小。这道题基本思路是贪心+字符串模拟,贪心之处在于这道题不需要非常复杂的技巧,只需要从最简单的位运算入手,分类讨论即可。
首先要引入两个工具函数,bitCount和bitLength,它们的作用和实现如下:
1.bitCount:计数一个数字的二进制表示中中有多少个1
int bitCount(int num)
{
int Ans = 0;
while(num)
{
if(num % 2)
++Ans;
num >>= 1;
}
return Ans;
}
2.bitLength:计数一个数字需要多少个二进制位来表达
int bitLength(int num)
{
int Ans = 0;
while(num)
{
++Ans;
num >>= 1;
}
return Ans;
}
在上述两个工具函数的帮助下,针对这个问题,可以分如下几种情况来讨论:
1.如果num1的二进制长度还没有num2二进制表示中1的数量多(BitLength1 < BitCount2),那么这个时候我们只需要将num2中已有的1全部聚集在较低位上即可。
2.如果num1和num2的二进制表示中1的数量一样多(BitCount1 = BitCount2),那么这个时候与num1抑或最小的数字x一定是num1本身,直接返回之即可。
3.如果num1的二进制表示中1的数量比num2少(BitCount1 < BitCount2),那么这个时候只需要将数字num1中从低位开始的0挨个置为1即可,因为我们得保证XOR之后数字尽可能小,所以应该首先将多出来的1匀在低位上。
4.如果num1的二进制表示中1的数量比num2多(BitCount1 > BitCount2),那么这个时候只需要将数字num1中从低位开始的1挨个置为0即可,因为我们要保证num2的那些1中和掉num1中较高位上的1,所以只能先牺牲一部分留在低位上的1。
综上所述,完整的代码如下:
class Solution {
int bitCount(int num)
{
int Ans = 0;
while(num)
{
if(num % 2)
++Ans;
num >>= 1;
}
return Ans;
}
int bitLength(int num)
{
int Ans = 0;
while(num)
{
++Ans;
num >>= 1;
}
return Ans;
}
int binaryStrToNum(string s)
{
int Ans = 0;
int Size = s.size();
for(int i = 0 ; i < Size ; ++i)
{
Ans = Ans << 1;
Ans += s[i] - '0';
}
return Ans;
}
string numToBinaryStr(int num)
{
string Ans = "";
while(num)
{
Ans.push_back(static_cast<char>(num % 2) + '0');
num >>= 1;
}
reverse(Ans.begin(), Ans.end());
return Ans;
}
public:
int minimizeXor(int num1, int num2) {
int BitCount1 = bitCount(num1);
int BitLength1 = bitLength(num1);
int BitCount2 = bitCount(num2);
if(BitCount2 >= BitLength1)
return (1 << BitCount2) - 1;
if(BitCount1 == BitCount2)
return num1;
string NumStr = numToBinaryStr(num1);
int Size = NumStr.size();
if(BitCount1 < BitCount2)
{
int Diff = BitCount2 - BitCount1;
for(int i = Size - 1 ; i >= 0 && Diff > 0 ; --i)
if(NumStr[i] == '0')
{
NumStr[i] = '1';
--Diff;
}
}
else if(BitCount1 > BitCount2)
{
int Diff = BitCount1 - BitCount2;
for(int i = Size - 1 ; i >= 0 && Diff > 0 ; --i)
if(NumStr[i] == '1')
{
NumStr[i] = '0';
--Diff;
}
}
return binaryStrToNum(NumStr);
}
};
这道题如果使用位运算的技巧的话,可以比较省代码量,但是思维难度较高,如果用字符串模拟的话,就比较耗费代码量(如上所示)。
2430. 对字母串可执行的最大删除数
CopyRight:灵茶山艾府
这道题最终要转化为一个最终公共前缀问题(线性DP),然后基于此去考虑最大删除次数。
这道题给出的AC代码如下, 题解链接在此,不再重复解释了,多多理解学习吧,我是小菜鸡捏(逃…)
class Solution {
public:
int deleteString(string s) {
int n = s.length();
if (equal(s.begin() + 1, s.end(), s.begin())) // 特判全部相同的情况
return n;
int lcp[n + 1][n + 1]; // lcp[i][j] 表示 s[i:] 和 s[j:] 的最长公共前缀
memset(lcp, 0, sizeof(lcp));
for (int i = n - 1; i >= 0; --i)
for (int j = n - 1; j > i; --j)
if (s[i] == s[j])
lcp[i][j] = lcp[i + 1][j + 1] + 1;
int f[n];
memset(f, 0, sizeof(f));
for (int i = n - 1; i >= 0; --i) {
for (int j = 1; i + j * 2 <= n; ++j)
if (lcp[i][i + j] >= j) // 说明 s[i:i+j] == s[i+j:i+j*2]
f[i] = max(f[i], f[i + j]);
++f[i];
}
return f[0];
}
};