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.
Move from brute-force thinking to an efficient approach using array strategy.
A transaction is possibly invalid if:
$1000, or;60 minutes of another transaction with the same name in a different city.You are given an array of strings transaction where transactions[i] consists of comma-separated values representing the name, time (in minutes), amount, and city of the transaction.
Return a list of transactions that are possibly invalid. You may return the answer in any order.
Example 1:
Input: transactions = ["alice,20,800,mtv","alice,50,100,beijing"] Output: ["alice,20,800,mtv","alice,50,100,beijing"] Explanation: The first transaction is invalid because the second transaction occurs within a difference of 60 minutes, have the same name and is in a different city. Similarly the second one is invalid too.
Example 2:
Input: transactions = ["alice,20,800,mtv","alice,50,1200,mtv"] Output: ["alice,50,1200,mtv"]
Example 3:
Input: transactions = ["alice,20,800,mtv","bob,50,1200,mtv"] Output: ["bob,50,1200,mtv"]
Constraints:
transactions.length <= 1000transactions[i] takes the form "{name},{time},{amount},{city}"{name} and {city} consist of lowercase English letters, and have lengths between 1 and 10.{time} consist of digits, and represent an integer between 0 and 1000.{amount} consist of digits, and represent an integer between 0 and 2000.Problem summary: A transaction is possibly invalid if: the amount exceeds $1000, or; if it occurs within (and including) 60 minutes of another transaction with the same name in a different city. You are given an array of strings transaction where transactions[i] consists of comma-separated values representing the name, time (in minutes), amount, and city of the transaction. Return a list of transactions that are possibly invalid. You may return the answer in any order.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Hash Map
["alice,20,800,mtv","alice,50,100,beijing"]
["alice,20,800,mtv","alice,50,1200,mtv"]
["alice,20,800,mtv","bob,50,1200,mtv"]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1169: Invalid Transactions
class Solution {
public List<String> invalidTransactions(String[] transactions) {
Map<String, List<Item>> d = new HashMap<>();
Set<Integer> idx = new HashSet<>();
for (int i = 0; i < transactions.length; ++i) {
var e = transactions[i].split(",");
String name = e[0];
int time = Integer.parseInt(e[1]);
int amount = Integer.parseInt(e[2]);
String city = e[3];
d.computeIfAbsent(name, k -> new ArrayList<>()).add(new Item(time, city, i));
if (amount > 1000) {
idx.add(i);
}
for (Item item : d.get(name)) {
if (!city.equals(item.city) && Math.abs(time - item.t) <= 60) {
idx.add(i);
idx.add(item.i);
}
}
}
List<String> ans = new ArrayList<>();
for (int i : idx) {
ans.add(transactions[i]);
}
return ans;
}
}
class Item {
int t;
String city;
int i;
Item(int t, String city, int i) {
this.t = t;
this.city = city;
this.i = i;
}
}
// Accepted solution for LeetCode #1169: Invalid Transactions
func invalidTransactions(transactions []string) (ans []string) {
d := map[string][]tuple{}
idx := map[int]bool{}
for i, x := range transactions {
e := strings.Split(x, ",")
name := e[0]
time, _ := strconv.Atoi(e[1])
amount, _ := strconv.Atoi(e[2])
city := e[3]
d[name] = append(d[name], tuple{time, city, i})
if amount > 1000 {
idx[i] = true
}
for _, item := range d[name] {
if city != item.city && abs(time-item.t) <= 60 {
idx[i], idx[item.i] = true, true
}
}
}
for i := range idx {
ans = append(ans, transactions[i])
}
return
}
func abs(x int) int {
if x < 0 {
return -x
}
return x
}
type tuple struct {
t int
city string
i int
}
# Accepted solution for LeetCode #1169: Invalid Transactions
class Solution:
def invalidTransactions(self, transactions: List[str]) -> List[str]:
d = defaultdict(list)
idx = set()
for i, x in enumerate(transactions):
name, time, amount, city = x.split(",")
time, amount = int(time), int(amount)
d[name].append((time, city, i))
if amount > 1000:
idx.add(i)
for t, c, j in d[name]:
if c != city and abs(time - t) <= 60:
idx.add(i)
idx.add(j)
return [transactions[i] for i in idx]
// Accepted solution for LeetCode #1169: Invalid Transactions
struct Solution;
use std::collections::HashMap;
use std::collections::HashSet;
impl Solution {
fn invalid_transactions(transactions: Vec<String>) -> Vec<String> {
let n = transactions.len();
let mut hm: HashMap<String, Vec<(i32, String, String)>> = HashMap::new();
let mut hs: HashSet<String> = HashSet::new();
for i in 0..n {
let transaction = &transactions[i];
let mut v: Vec<String> = transaction
.split_terminator(',')
.map(|s| s.to_string())
.collect();
let city = v.pop().unwrap();
let amount = v.pop().unwrap().parse::<i32>().unwrap();
let time = v.pop().unwrap().parse::<i32>().unwrap();
let name = v.pop().unwrap();
hm.entry(name)
.or_default()
.push((time, city, transaction.clone()));
if amount > 1000 {
hs.insert(transactions[i].clone());
}
}
for transactions in hm.values() {
let n = transactions.len();
for i in 0..n {
for j in i + 1..n {
let ti = &transactions[i];
let tj = &transactions[j];
if (ti.0 - tj.0).abs() <= 60 && ti.1 != tj.1 {
hs.insert(ti.2.clone());
hs.insert(tj.2.clone());
}
}
}
}
hs.drain().collect()
}
}
#[test]
fn test() {
let transactions = vec_string!["alice,20,800,mtv", "alice,50,100,beijing"];
let mut res = vec_string!["alice,20,800,mtv", "alice,50,100,beijing"];
let mut ans = Solution::invalid_transactions(transactions);
res.sort();
ans.sort();
assert_eq!(ans, res);
let transactions = vec_string!["alice,20,800,mtv", "alice,50,1200,mtv"];
let mut res = vec_string!["alice,50,1200,mtv"];
let mut ans = Solution::invalid_transactions(transactions);
res.sort();
ans.sort();
assert_eq!(ans, res);
let transactions = vec_string!["alice,20,800,mtv", "bob,50,1200,mtv"];
let mut res = vec_string!["bob,50,1200,mtv"];
let mut ans = Solution::invalid_transactions(transactions);
res.sort();
ans.sort();
assert_eq!(ans, res);
}
// Accepted solution for LeetCode #1169: Invalid Transactions
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #1169: Invalid Transactions
// class Solution {
// public List<String> invalidTransactions(String[] transactions) {
// Map<String, List<Item>> d = new HashMap<>();
// Set<Integer> idx = new HashSet<>();
// for (int i = 0; i < transactions.length; ++i) {
// var e = transactions[i].split(",");
// String name = e[0];
// int time = Integer.parseInt(e[1]);
// int amount = Integer.parseInt(e[2]);
// String city = e[3];
// d.computeIfAbsent(name, k -> new ArrayList<>()).add(new Item(time, city, i));
// if (amount > 1000) {
// idx.add(i);
// }
// for (Item item : d.get(name)) {
// if (!city.equals(item.city) && Math.abs(time - item.t) <= 60) {
// idx.add(i);
// idx.add(item.i);
// }
// }
// }
// List<String> ans = new ArrayList<>();
// for (int i : idx) {
// ans.add(transactions[i]);
// }
// return ans;
// }
// }
//
// class Item {
// int t;
// String city;
// int i;
//
// Item(int t, String city, int i) {
// this.t = t;
// this.city = city;
// this.i = 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.