2353. Design a Food Rating System
Difficulty: Medium
Topics: Array
, Hash Table
, String
, Design
, Heap (Priority Queue)
, Ordered Set
, Weekly Contest 303
Design a food rating system that can do the following:
- Modify the rating of a food item listed in the system.
- Return the highest-rated food item for a type of cuisine in the system.
Implement the FoodRatings
class:
-
FoodRatings(String[] foods, String[] cuisines, int[] ratings)
Initializes the system. The food items are described byfoods
,cuisines
andratings
, all of which have a length ofn
.-
foods[i]
is the name of theith
food, -
cuisines[i]
is the type of cuisine of theith
food, and -
ratings[i]
is the initial rating of theith
food.
-
-
void changeRating(String food, int newRating)
Changes the rating of the food item with the namefood
. -
String highestRated(String cuisine)
Returns the name of the food item that has the highest rating for the given type ofcuisine
. If there is a tie, return the item with the lexicographically smaller name.
Note that a string x
is lexicographically smaller than string y
if x
comes before y
in dictionary order, that is, either x
is a prefix of y
, or if i
is the first position such that x[i] != y[i]
, then x[i]
comes before y[i]
in alphabetic order.
Example 1:
- Input:
["FoodRatings", "highestRated", "highestRated", "changeRating", "highestRated", "changeRating", "highestRated"]
[[["kimchi", "miso", "sushi", "moussaka", "ramen", "bulgogi"], ["korean", "japanese", "japanese", "greek", "japanese", "korean"], [9, 12, 8, 15, 14, 7]], ["korean"], ["japanese"], ["sushi", 16], ["japanese"], ["ramen", 16], ["japanese"]]
- Output: [null, "kimchi", "ramen", null, "sushi", null, "ramen"]
- Explanation:
FoodRatings foodRatings = new FoodRatings(["kimchi", "miso", "sushi", "moussaka", "ramen", "bulgogi"], ["korean", "japanese", "japanese", "greek", "japanese", "korean"], [9, 12, 8, 15, 14, 7]);
foodRatings.highestRated("korean"); // return "kimchi"
// "kimchi" is the highest rated korean food with a rating of 9.
foodRatings.highestRated("japanese"); // return "ramen"
// "ramen" is the highest rated japanese food with a rating of 14.
foodRatings.changeRating("sushi", 16); // "sushi" now has a rating of 16.
foodRatings.highestRated("japanese"); // return "sushi"
// "sushi" is the highest rated japanese food with a rating of 16.
foodRatings.changeRating("ramen", 16); // "ramen" now has a rating of 16.
foodRatings.highestRated("japanese"); // return "ramen"
// Both "sushi" and "ramen" have a rating of 16.
// However, "ramen" is lexicographically smaller than "sushi".
Constraints:
1 <= n <= 2 * 104
n == foods.length == cuisines.length == ratings.length
1 <= foods[i].length, cuisines[i].length <= 10
-
foods[i]
,cuisines[i]
consist of lowercase English letters. 1 <= ratings[i] <= 108
- All the strings in
foods
are distinct. -
food
will be the name of a food item in the system across all calls tochangeRating
. -
cuisine
will be a type of cuisine of at least one food item in the system across all calls tohighestRated
. - At most
2 * 104
calls in total will be made tochangeRating
andhighestRated
.
Hint:
- The key to solving this problem is to properly store the data using the right data structures.
- Firstly, a hash table is needed to efficiently map each food item to its cuisine and current rating.
- In addition, another hash table is needed to map cuisines to foods within each cuisine stored in an ordered set according to their ratings.
Solution:
We need to design a food rating system that supports modifying the rating of a food item and retrieving the highest-rated food item for a specific cuisine. The solution involves efficiently storing and updating food ratings while ensuring quick access to the highest-rated food for any given cuisine, considering lexicographical order in case of ties.
Approach
-
Data Structures:
-
foodInfo
: A hash map that stores each food's cuisine and current rating. -
cuisineHeaps
: A hash map where each key is a cuisine and the value is a max-heap (priority queue) that contains pairs of ratings and food names. The heap is ordered such that higher ratings come first, and in case of ties, lexicographically smaller food names are considered larger (so they appear at the top).
-
-
Initialization:
- For each food item, store its cuisine and rating in
foodInfo
. - For each cuisine, initialize a max-heap (if not already present) and insert the initial rating and food name.
- For each food item, store its cuisine and rating in
-
Change Rating:
- Update the food's rating in
foodInfo
. - Push the new rating and food name into the corresponding cuisine's heap. This approach uses lazy deletion, meaning the heap may contain outdated entries, which are handled during retrieval.
- Update the food's rating in
-
Highest Rated:
- Retrieve the top element from the cuisine's heap. If the rating in the heap matches the current rating in
foodInfo
, return the food name. Otherwise, pop the outdated entry and continue until a valid entry is found.
- Retrieve the top element from the cuisine's heap. If the rating in the heap matches the current rating in
Let's implement this solution in PHP: 2353. Design a Food Rating System
<?php
class FoodRatings {
private $foodInfo;
private $cuisineHeaps;
/**
* @param String[] $foods
* @param String[] $cuisines
* @param Integer[] $ratings
*/
function __construct($foods, $cuisines, $ratings) {
...
...
...
/**
* go to ./solution.php
*/
}
/**
* @param String $food
* @param Integer $newRating
* @return NULL
*/
function changeRating($food, $newRating) {
...
...
...
/**
* go to ./solution.php
*/
}
/**
* @param String $cuisine
* @return String
*/
function highestRated($cuisine) {
...
...
...
/**
* go to ./solution.php
*/
}
}
class MyMaxHeap extends SplMaxHeap {
/**
* @param $a
* @param $b
* @return int
*/
public function compare($a, $b): int {
...
...
...
/**
* go to ./solution.php
*/
}
}
/**
* Your FoodRatings object will be instantiated and called as such:
* $obj = FoodRatings($foods, $cuisines, $ratings);
* $obj->changeRating($food, $newRating);
* $ret_2 = $obj->highestRated($cuisine);
*/
// Test cases
$foods = ["kimchi", "miso", "sushi", "moussaka", "ramen", "bulgogi"];
$cuisines = ["korean", "japanese", "japanese", "greek", "japanese", "korean"];
$ratings = [9, 12, 8, 15, 14, 7];
$obj = new FoodRatings($foods, $cuisines, $ratings);
echo $obj->highestRated("korean") . PHP_EOL; // kimchi
echo $obj->highestRated("japanese") . PHP_EOL; // ramen
$obj->changeRating("sushi", 16);
echo $obj->highestRated("japanese") . PHP_EOL; // sushi
$obj->changeRating("ramen", 16);
echo $obj->highestRated("japanese") . PHP_EOL; // ramen
?>
Explanation:
-
Initialization: The constructor initializes two dictionaries:
foodInfo
stores each food's cuisine and current rating, andcuisineHeaps
stores max-heaps for each cuisine. Each heap is initialized with the initial ratings and food names. -
Change Rating: The
changeRating
method updates the food's rating infoodInfo
and pushes the new rating into the corresponding cuisine's heap. This ensures that the heap always has the most recent rating, though outdated entries may remain. -
Highest Rated: The
highestRated
method checks the top element of the cuisine's heap. If the rating matches the current rating infoodInfo
, it returns the food name. Otherwise, it pops the outdated entry and repeats the process until a valid entry is found. This lazy deletion ensures that the heap remains efficient for insertions and deletions while providing accurate results.
This approach efficiently manages dynamic updates and queries by leveraging hash maps and heaps, ensuring optimal performance for both operations. The lazy deletion strategy minimizes the overhead of updating existing heap entries, making the solution both practical and efficient.
Contact Links
If you found this series helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!
If you want more helpful content like this, feel free to follow me:
Top comments (0)