2353. Design a Food Rating System
Description
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 by- foods,- cuisinesand- ratings, all of which have a length of- n.- foods[i]is the name of the- ithfood,
- cuisines[i]is the type of cuisine of the- ithfood, and
- ratings[i]is the initial rating of the- ithfood.
 
- void changeRating(String food, int newRating)Changes the rating of the food item with the name- food.
- String highestRated(String cuisine)Returns the name of the food item that has the highest rating for the given type of- cuisine. 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 foodsare distinct.
- foodwill be the name of a food item in the system across all calls to- changeRating.
- cuisinewill be a type of cuisine of at least one food item in the system across all calls to- highestRated.
- At most 2 * 104calls in total will be made tochangeRatingandhighestRated.
Solutions
Solution 1: Hash Table + Ordered Set
We can use a hash table \(\textit{d}\) to store the foods for each cuisine, where the key is the cuisine and the value is an ordered set. Each element in the ordered set is a tuple \((\textit{rating}, \textit{food})\), sorted by rating in descending order, and if the ratings are the same, sorted by food name in lexicographical order.
We can also use a hash table \(\textit{g}\) to store the rating and cuisine for each food. That is, \(\textit{g}[\textit{food}] = (\textit{rating}, \textit{cuisine})\).
In the constructor, we iterate through \(\textit{foods}\), \(\textit{cuisines}\), and \(\textit{ratings}\), storing the rating and cuisine for each food in \(\textit{d}\) and \(\textit{g}\).
In the \(\textit{changeRating}\) function, we first get the original rating \(\textit{oldRating}\) and cuisine \(\textit{cuisine}\) of the food \(\textit{food}\), then update the rating of \(\textit{g}[\textit{food}]\) to \(\textit{newRating}\), remove \((\textit{oldRating}, \textit{food})\) from \(\textit{d}[\textit{cuisine}]\), and add \((\textit{newRating}, \textit{food})\) to \(\textit{d}[\textit{cuisine}]\).
In the \(\textit{highestRated}\) function, we directly return the food name of the first element in \(\textit{d}[\textit{cuisine}]\).
In terms of time complexity, the constructor has a time complexity of \(O(n \log n)\), where \(n\) is the number of foods. The other operations have a time complexity of \(O(\log n)\). The space complexity is \(O(n)\).
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |  | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |  | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |  | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |  |