本文涉及的基础知识点
C++二分查找
LeetCode3143. 正方形中的最多点数
给你一个二维数组 points 和一个字符串 s ,其中 points[i] 表示第 i 个点的坐标,s[i] 表示第 i 个点的 标签 。
如果一个正方形的中心在 (0, 0) ,所有边都平行于坐标轴,且正方形内 不 存在标签相同的两个点,那么我们称这个正方形是 合法 的。
请你返回 合法 正方形中可以包含的 最多 点数。
注意:
如果一个点位于正方形的边上或者在边以内,则认为该点位于正方形内。
正方形的边长可以为零。
示例 1:
输入:points = [[2,2],[-1,-2],[-4,4],[-3,1],[3,-3]], s = “abdca”
输出:2
解释:
边长为 4 的正方形包含两个点 points[0] 和 points[1] 。
示例 2:
输入:points = [[1,1],[-2,-2],[-2,2]], s = “abb”
输出:1
解释:
边长为 2 的正方形包含 1 个点 points[0] 。
示例 3:
输入:points = [[1,1],[-1,-1],[2,-2]], s = “ccd”
输出:0
解释:
任何正方形都无法只包含 points[0] 和 points[1] 中的一个点,所以合法正方形中都不包含任何点。
提示:
1 <= s.length, points.length <= 105
points[i].length == 2
-109 <= points[i][0], points[i][1] <= 109
s.length == points.length
points 中的点坐标互不相同。
s 只包含小写英文字母。
分析
性质一:某正方形包含点(x,y),则一定包含(-x,y)和(x,-y)。不包含类似。如果坐标是负数,改成绝对值,结果不变。
性质二通过性质一转换后。如果x<y,某正方形包含(x,y),则一定包含(y,y)。即正方形包含(x,y)等效与包含(y,y)。同理,x > y。包含(x,y),等效于包含(x,x)。两者结合,m= max(x,y),包含(x,y), 等效于包含(m,m)。正方形不包含(x,y)等效与不包含(m,m)。我们只需要记录各点的m。即雪切夫距离:两个点之间的距离定义是其各坐标数值差绝对值的最大值。
性质三:正方形包含(m,m)的充分必要条件是:edge >= m。
如果各标签都只有一个点,返回点的数量。否则求包括同一标签两个点或以上的最小正方形,令其变成为s。返回边长为s-1的正方形包含点的数量。
方法一:排序。
分治法:分别记录各标签的雪切夫距离,并按升序排序。x[i][1]的最小值就是s。
方法二:C++二分法
二分类型:寻找首端。
Check函数的参数范围:[1,109]。
Check函数:
任意x[i],大于等于mid的数量大于1,返回true。Check函数的时间复杂度:O(n),枚举所有点。总时间复杂度:O(nlogm),m = max(points)
所以标签都只有一个点,提前返回点的数量。故一定有解。否则,需要判断是否有解。
代码
核心代码
template<class INDEX_TYPE>
class CBinarySearch
{
public:
CBinarySearch(INDEX_TYPE iMinIndex, INDEX_TYPE iMaxIndex):m_iMin(iMinIndex),m_iMax(iMaxIndex) {}
template<class _Pr>
INDEX_TYPE FindFrist( _Pr pr)
{
auto left = m_iMin - 1;
auto rightInclue = m_iMax;
while (rightInclue - left > 1)
{
const auto mid = left + (rightInclue - left) / 2;
if (pr(mid))
{
rightInclue = mid;
}
else
{
left = mid;
}
}
return rightInclue;
}
template<class _Pr>
INDEX_TYPE FindEnd( _Pr pr)
{
int leftInclude = m_iMin;
int right = m_iMax + 1;
while (right - leftInclude > 1)
{
const auto mid = leftInclude + (right - leftInclude) / 2;
if (pr(mid))
{
leftInclude = mid;
}
else
{
right = mid;
}
}
return leftInclude;
}
protected:
const INDEX_TYPE m_iMin, m_iMax;
};
class Solution {
public:
int maxPointsInsideSquare(vector<vector<int>>& points, string s) {
vector<int> ms[26];
for (int i = 0; i < points.size(); i++) {
ms[s[i] - 'a'].emplace_back(max(abs(points[i][0]), abs(points[i][1])));
}
auto Check = [&](int mid) {
for (const auto& v : ms) {
int cnt = 0;
for (const auto& n : v) {
cnt += (n <= mid);
}
if (cnt > 1) { return true; }
}
return false;
};
auto ret = CBinarySearch<int>(1, 1000'000'000).FindFrist(Check);
if (!Check(ret)) { return s.length(); }
int cnt = 0;
for (const auto& v : ms) {
for (const auto& n : v) {
cnt += (n < ret );
}
}
return cnt;
}
};
单元测试
vector<vector<int>> points;
string s;
TEST_METHOD(TestMethod11)
{
points = { {2,2},{-1,-2},{-4,4},{-3,1},{3,-3} }, s = "abdca";
auto res = Solution().maxPointsInsideSquare(points, s);
AssertEx(2, res);
}
TEST_METHOD(TestMethod12)
{
points = { {1,1},{-2,-2},{-2,2} }, s = "abb";
auto res = Solution().maxPointsInsideSquare(points, s);
AssertEx(1, res);
}
TEST_METHOD(TestMethod13)
{
points = { {1,1},{-1,-1},{2,-2} }, s = "ccd";
auto res = Solution().maxPointsInsideSquare(points, s);
AssertEx(0, res);
}
TEST_METHOD(TestMethod14)
{
points = { {16, 32}, { 27,3 }, { 23,-14 }, { -32,-16 }, { -3,26 }, { -14,33 }}, s = "aaabfc";
auto res = Solution().maxPointsInsideSquare(points, s);
AssertEx(2, res);
}
扩展阅读
我想对大家说的话 |
---|
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。 |
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作 |
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注 |
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
失败+反思=成功 成功+反思=成功 |
视频课程
先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。