Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Build confidence with an intuition-first walkthrough focused on array fundamentals.
You are given three arrays of length n that describe the properties of n coupons: code, businessLine, and isActive. The ith coupon has:
code[i]: a string representing the coupon identifier.businessLine[i]: a string denoting the business category of the coupon.isActive[i]: a boolean indicating whether the coupon is currently active.A coupon is considered valid if all of the following conditions hold:
code[i] is non-empty and consists only of alphanumeric characters (a-z, A-Z, 0-9) and underscores (_).businessLine[i] is one of the following four categories: "electronics", "grocery", "pharmacy", "restaurant".isActive[i] is true.Return an array of the codes of all valid coupons, sorted first by their businessLine in the order: "electronics", "grocery", "pharmacy", "restaurant", and then by code in lexicographical (ascending) order within each category.
Example 1:
Input: code = ["SAVE20","","PHARMA5","SAVE@20"], businessLine = ["restaurant","grocery","pharmacy","restaurant"], isActive = [true,true,true,true]
Output: ["PHARMA5","SAVE20"]
Explanation:
@ (invalid).Example 2:
Input: code = ["GROCERY15","ELECTRONICS_50","DISCOUNT10"], businessLine = ["grocery","electronics","invalid"], isActive = [false,true,true]
Output: ["ELECTRONICS_50"]
Explanation:
Constraints:
n == code.length == businessLine.length == isActive.length1 <= n <= 1000 <= code[i].length, businessLine[i].length <= 100code[i] and businessLine[i] consist of printable ASCII characters.isActive[i] is either true or false.Problem summary: You are given three arrays of length n that describe the properties of n coupons: code, businessLine, and isActive. The ith coupon has: code[i]: a string representing the coupon identifier. businessLine[i]: a string denoting the business category of the coupon. isActive[i]: a boolean indicating whether the coupon is currently active. A coupon is considered valid if all of the following conditions hold: code[i] is non-empty and consists only of alphanumeric characters (a-z, A-Z, 0-9) and underscores (_). businessLine[i] is one of the following four categories: "electronics", "grocery", "pharmacy", "restaurant". isActive[i] is true. Return an array of the codes of all valid coupons, sorted first by their businessLine in the order: "electronics", "grocery", "pharmacy", "restaurant", and then by code in lexicographical (ascending) order within each category.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map
["SAVE20","","PHARMA5","SAVE@20"] ["restaurant","grocery","pharmacy","restaurant"] [true,true,true,true]
["GROCERY15","ELECTRONICS_50","DISCOUNT10"] ["grocery","electronics","invalid"] [false,true,true]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3606: Coupon Code Validator
class Solution {
public List<String> validateCoupons(String[] code, String[] businessLine, boolean[] isActive) {
List<Integer> idx = new ArrayList<>();
Set<String> bs
= new HashSet<>(Arrays.asList("electronics", "grocery", "pharmacy", "restaurant"));
for (int i = 0; i < code.length; i++) {
if (isActive[i] && bs.contains(businessLine[i]) && check(code[i])) {
idx.add(i);
}
}
idx.sort((i, j) -> {
int cmp = businessLine[i].compareTo(businessLine[j]);
if (cmp != 0) {
return cmp;
}
return code[i].compareTo(code[j]);
});
List<String> ans = new ArrayList<>();
for (int i : idx) {
ans.add(code[i]);
}
return ans;
}
private boolean check(String s) {
if (s.isEmpty()) {
return false;
}
for (char c : s.toCharArray()) {
if (!Character.isLetterOrDigit(c) && c != '_') {
return false;
}
}
return true;
}
}
// Accepted solution for LeetCode #3606: Coupon Code Validator
func validateCoupons(code []string, businessLine []string, isActive []bool) []string {
idx := []int{}
bs := map[string]struct{}{
"electronics": {},
"grocery": {},
"pharmacy": {},
"restaurant": {},
}
check := func(s string) bool {
if len(s) == 0 {
return false
}
for _, c := range s {
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' {
return false
}
}
return true
}
for i := range code {
if isActive[i] {
if _, ok := bs[businessLine[i]]; ok && check(code[i]) {
idx = append(idx, i)
}
}
}
sort.Slice(idx, func(i, j int) bool {
if businessLine[idx[i]] != businessLine[idx[j]] {
return businessLine[idx[i]] < businessLine[idx[j]]
}
return code[idx[i]] < code[idx[j]]
})
ans := make([]string, 0, len(idx))
for _, i := range idx {
ans = append(ans, code[i])
}
return ans
}
# Accepted solution for LeetCode #3606: Coupon Code Validator
class Solution:
def validateCoupons(
self, code: List[str], businessLine: List[str], isActive: List[bool]
) -> List[str]:
def check(s: str) -> bool:
if not s:
return False
for c in s:
if not (c.isalpha() or c.isdigit() or c == "_"):
return False
return True
idx = []
bs = {"electronics", "grocery", "pharmacy", "restaurant"}
for i, (c, b, a) in enumerate(zip(code, businessLine, isActive)):
if a and b in bs and check(c):
idx.append(i)
idx.sort(key=lambda i: (businessLine[i], code[i]))
return [code[i] for i in idx]
// Accepted solution for LeetCode #3606: Coupon Code Validator
use std::collections::HashSet;
impl Solution {
pub fn validate_coupons(
code: Vec<String>,
business_line: Vec<String>,
is_active: Vec<bool>,
) -> Vec<String> {
fn check(s: &str) -> bool {
if s.is_empty() {
return false;
}
s.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_')
}
let bs: HashSet<&str> =
["electronics", "grocery", "pharmacy", "restaurant"]
.iter()
.copied()
.collect();
let mut idx: Vec<usize> = Vec::new();
for i in 0..code.len() {
if is_active[i] && bs.contains(business_line[i].as_str()) && check(&code[i]) {
idx.push(i);
}
}
idx.sort_by(|&i, &j| {
let cmp = business_line[i].cmp(&business_line[j]);
if cmp == std::cmp::Ordering::Equal {
code[i].cmp(&code[j])
} else {
cmp
}
});
idx.into_iter().map(|i| code[i].clone()).collect()
}
}
// Accepted solution for LeetCode #3606: Coupon Code Validator
function validateCoupons(code: string[], businessLine: string[], isActive: boolean[]): string[] {
const idx: number[] = [];
const bs = new Set(['electronics', 'grocery', 'pharmacy', 'restaurant']);
const check = (s: string): boolean => {
if (s.length === 0) return false;
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (!/[a-zA-Z0-9_]/.test(c)) {
return false;
}
}
return true;
};
for (let i = 0; i < code.length; i++) {
if (isActive[i] && bs.has(businessLine[i]) && check(code[i])) {
idx.push(i);
}
}
idx.sort((i, j) => {
if (businessLine[i] !== businessLine[j]) {
return businessLine[i] < businessLine[j] ? -1 : 1;
}
return code[i] < code[j] ? -1 : 1;
});
return idx.map(i => code[i]);
}
Use this to step through a reusable interview workflow for this problem.
Two nested loops check every pair or subarray. The outer loop fixes a starting point, the inner loop extends or searches. For n elements this gives up to n²/2 operations. No extra space, but the quadratic time is prohibitive for large inputs.
Most array problems have an O(n²) brute force (nested loops) and an O(n) optimal (single pass with clever state tracking). The key is identifying what information to maintain as you scan: a running max, a prefix sum, a hash map of seen values, or two pointers.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.