3143. Maximum Points Inside the Square
Problem Description
You are given a 2D array points
and a string s
where points[i]
represents the coordinates of point i
, and s[i]
represents the tag of point i
.
A valid square is a square centered at the origin (0, 0)
, has edges parallel to the axes, and does not contain two points with the same tag.
Return the maximum number of points contained in a valid square.
Note:
- A point is considered to be inside the square if it lies on or within the square's boundaries.
- The side length of the square can be zero.
Intuition
To solve this problem, consider the properties of the squares centered at the origin with edges parallel to the axes. For any point (x, y)
, determine the edge length of the square such that the point lies on the boundary of or within the square. This can be calculated as max(abs(x), abs(y))
, giving the distance to the furthest axis-aligned component from the origin.
The core idea is to group points based on this distance and consider consecutive increasing distances, starting from zero, adding layers of points into the square as long as a valid square configuration is maintained. A valid configuration requires that no two points within the current square share the same tag.
By storing these distances and corresponding points in a hash table and keeping track of the tags using a set while iterating through sorted distances, we can count the maximum number of valid points. If any duplicate tags are encountered while processing a group of points, the traversal stops for that square size, and the current count is returned as the solution.
Learn more about Binary Search and Sorting patterns.
Solution Approach
The solution uses a combination of a hash table and sorting to efficiently determine the maximum number of points that can fit inside a valid square while adhering to the tag constraint. Here’s the detailed breakdown:
-
Hash Table Construction:
- Iterate over each point
(x, y)
in thepoints
array. - Calculate the distance
d
from the origin to the boundary of the prospective square. This distance ismax(abs(x), abs(y))
. - Use this distance
d
as a key in a hash tableg
, where each key stores a list of indices of points that have the same distanced
from the origin. This grouping helps in processing points layer by layer starting from the smallest squares.
- Iterate over each point
-
Distance Sorting:
- Sort the distances stored in the hash table. This ensures that we process the points starting from the smallest possible square (which is crucial for maximizing the number of points).
-
Maintaining Validity with Tags:
- Initialize a set
vis
to keep track of the tags of the points included in the current valid square. - Initialize a counter
ans
to store the maximum number of valid points inside a square. - Iterate over each distance
d
in the sorted order:- Retrieve all point indices corresponding to this distance.
- For each point index:
- Check the tag
s[i]
of the point. If this tag is already invis
, it implies adding this point would violate the unique tag constraint, so break the loop and returnans
. - Otherwise, add the tag to
vis
.
- Check the tag
- Add the number of points processed at this distance to
ans
.
- Initialize a set
-
Result:
- After processing all possible layers without violating the constraints,
ans
contains the maximum number of points that can fit in a valid square.
- After processing all possible layers without violating the constraints,
The use of a hash table helps in efficiently grouping points, while sorting ensures the correctness of layering points into the square.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's illustrate the solution approach with a small example.
Suppose you have the following points represented by a 2D array points
and their corresponding tags in a string s
:
points = [[1, 2], [-1, -2], [3, 1], [-3, 1]] s = "abcd"
Step-by-Step Breakdown:
-
Hash Table Construction:
- Calculate the distance
d
from the origin for each point, using the formulamax(abs(x), abs(y))
.- For point
(1, 2)
,d = max(abs(1), abs(2)) = 2
. - For point
(-1, -2)
,d = max(abs(-1), abs(-2)) = 2
. - For point
(3, 1)
,d = max(abs(3), abs(1)) = 3
. - For point
(-3, 1)
,d = max(abs(-3), abs(1)) = 3
.
- For point
- Build a hash table
g
:g = {2: [0, 1], 3: [2, 3]}
.
- Calculate the distance
-
Distance Sorting:
- Sort the keys (distances) in
g
to get[2, 3]
.
- Sort the keys (distances) in
-
Maintaining Validity with Tags:
-
Initialize
vis = set()
to track unique tags. -
Initialize
ans = 0
to store the maximum valid points. -
Iterate over sorted distances:
For distance 2:
- Points with indices
[0, 1]
correspond to tags'a'
and'b'
. - Neither
'a'
nor'b'
are invis
. - Add
'a'
and'b'
tovis
. - Increment
ans
by 2 (ans = 2
).
For distance 3:
- Points with indices
[2, 3]
correspond to tags'c'
and'd'
. - Neither
'c'
nor'd'
are invis
. - Add
'c'
and'd'
tovis
. - Increment
ans
by 2 (ans = 4
).
- Points with indices
-
-
Result:
- After processing,
ans
is 4. This is the maximum number of points that can fit in a valid square at the origin with no duplicate tags.
- After processing,
This approach ensures we count the maximum number of points following the rules of tags and distance from the origin. The key lies in grouping points by their distance from the origin and maintaining a unique set of tags.
Solution Implementation
1from collections import defaultdict
2from typing import List
3
4class Solution:
5 def maxPointsInsideSquare(self, points: List[List[int]], s: str) -> int:
6 point_groups = defaultdict(list) # Dictionary to group points by their distance from the origin
7 # Iterate over the list of points with their indices
8 for index, (x, y) in enumerate(points):
9 # Group points by the maximum of the absolute x and y coordinates
10 point_groups[max(abs(x), abs(y))].append(index)
11
12 visited_chars = set() # Set to keep track of visited characters in 's'
13 max_points = 0 # Counter to track the number of points inside the square
14
15 # Iterate over the sorted distances (keys) in the dictionary
16 for distance in sorted(point_groups):
17 indices = point_groups[distance]
18
19 for i in indices:
20 # Check if the character at this index in 's' is already visited
21 if s[i] in visited_chars:
22 return max_points # Return the count if the character is already visited
23
24 visited_chars.add(s[i]) # Mark the character as visited
25
26 max_points += len(indices) # Increment the count by the number of points in this distance group
27
28 return max_points # Return the total number of points counted inside the square
29
1import java.util.*;
2
3class Solution {
4 public int maxPointsInsideSquare(int[][] points, String s) {
5 // TreeMap to store lists of indices by their maximum absolute coordinate
6 TreeMap<Integer, List<Integer>> coordinateMap = new TreeMap<>();
7
8 // Populate the TreeMap with point indices grouped by the maximum absolute value of their coordinates
9 for (int i = 0; i < points.length; ++i) {
10 int x = points[i][0], y = points[i][1];
11 // Determine the maximum absolute value between the x and y coordinates
12 int maxCoordinate = Math.max(Math.abs(x), Math.abs(y));
13 // Add the index to the corresponding list in the TreeMap
14 coordinateMap.computeIfAbsent(maxCoordinate, k -> new ArrayList<>()).add(i);
15 }
16
17 // Boolean array to track the alphabetic characters that have been seen (0 for 'a', 1 for 'b', ..., 25 for 'z')
18 boolean[] visited = new boolean[26];
19 int maxPoints = 0;
20
21 // Iterate over the values (lists of indices) in order of their keys (maximum absolute coordinates)
22 for (List<Integer> indices : coordinateMap.values()) {
23 for (int index : indices) {
24 // Get the current character from the string s and calculate its position in the alphabet
25 int charIndex = s.charAt(index) - 'a';
26
27 // If the character has been seen before, return the current maxPoints as no new characters can be added
28 if (visited[charIndex]) {
29 return maxPoints;
30 }
31 // Mark the character as seen
32 visited[charIndex] = true;
33 }
34 // Increment the count of maxPoints by the size of the list of indices
35 maxPoints += indices.size();
36 }
37
38 // Return the total number of points that can fit inside the square
39 return maxPoints;
40 }
41}
42
1#include <vector>
2#include <string>
3#include <map>
4
5class Solution {
6public:
7 // Function to determine the maximum number of points that can be inside a square.
8 int maxPointsInsideSquare(std::vector<std::vector<int>>& points, std::string s) {
9 std::map<int, std::vector<int>> groupedPoints; // Map to hold points based on maximum of their absolute coordinates.
10
11 // Group points by the maximum of absolute x or y values as keys.
12 for (int i = 0; i < points.size(); ++i) {
13 auto& point = points[i];
14 int key = std::max(abs(point[0]), abs(point[1]));
15 groupedPoints[key].push_back(i);
16 }
17
18 bool visited[26] = {}; // Array to track visited characters (a-z).
19 int result = 0; // Initialize the result.
20
21 // Iterate through each group of points.
22 for (auto& [key, indices] : groupedPoints) {
23 for (int i : indices) {
24 int charIndex = s[i] - 'a'; // Determine the character index corresponding to point.
25 if (visited[charIndex]) { // If character has been seen already, return answer.
26 return result;
27 }
28 visited[charIndex] = true; // Mark this character as visited.
29 }
30 result += indices.size(); // Add number of points to the result for this key.
31 }
32 return result; // Return the final computed result.
33 }
34};
35
1function maxPointsInsideSquare(points: number[][], s: string): number {
2 const n = points.length;
3
4 // Map to group indices of points by the maximum absolute coordinate value
5 const indexMap: Map<number, number[]> = new Map();
6
7 // Populate the map with indices of points
8 for (let i = 0; i < n; ++i) {
9 const [x, y] = points[i];
10 // Calculate the key as the maximum of the absolute x or y
11 const key = Math.max(Math.abs(x), Math.abs(y));
12
13 // Initialize array in the map at this key if it doesn't exist
14 if (!indexMap.has(key)) {
15 indexMap.set(key, []);
16 }
17
18 // Append the index to the relevant key's array
19 indexMap.get(key)!.push(i);
20 }
21
22 // Get sorted keys of the map
23 const keys = Array.from(indexMap.keys()).sort((a, b) => a - b);
24
25 // Array to check if a character in the input string s has been visited/used
26 const visited: boolean[] = Array(26).fill(false);
27
28 let maxPoints = 0;
29
30 // Iterate over each key
31 for (const key of keys) {
32 const indices = indexMap.get(key)!; // Retrieve indices for the current key
33
34 // Check each index to see if we've seen the corresponding character before
35 for (const i of indices) {
36 const charIndex = s.charCodeAt(i) - 'a'.charCodeAt(0);
37
38 // If character already visited, return the count so far
39 if (visited[charIndex]) {
40 return maxPoints;
41 }
42
43 // Mark the character as visited
44 visited[charIndex] = true;
45 }
46
47 // Increment maxPoints count by the number of points at this key
48 maxPoints += indices.length;
49 }
50
51 // Return the total number of points
52 return maxPoints;
53}
54
Time and Space Complexity
The time complexity of the code is O(n × log n)
. This is because the code sorts a list containing the keys of the defaultdict g
, which has at most n
elements, where each element corresponds to a unique maximum distance from the origin for a point. Sorting this list requires O(n × log n)
operations. Then, iterating through each list in g
and checking membership in a set vis
contributes O(n)
operations.
The space complexity of the code is O(n)
. This is due to the storage of the indices of points in the defaultdict g
, which holds indices for each potential value of maximum distance, and also due to the storage of characters in the set vis
, both of which are proportional to n
.
Learn more about how to find time and space complexity quickly.
Which of the following uses divide and conquer strategy?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!