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 core interview patterns strategy.
Suppose you are given the following code:
class FooBar {
public void foo() {
for (int i = 0; i < n; i++) {
print("foo");
}
}
public void bar() {
for (int i = 0; i < n; i++) {
print("bar");
}
}
}
The same instance of FooBar will be passed to two different threads:
A will call foo(), whileB will call bar().Modify the given program to output "foobar" n times.
Example 1:
Input: n = 1 Output: "foobar" Explanation: There are two threads being fired asynchronously. One of them calls foo(), while the other calls bar(). "foobar" is being output 1 time.
Example 2:
Input: n = 2 Output: "foobarfoobar" Explanation: "foobar" is being output 2 times.
Constraints:
1 <= n <= 1000Problem summary: Suppose you are given the following code: class FooBar { public void foo() { for (int i = 0; i < n; i++) { print("foo"); } } public void bar() { for (int i = 0; i < n; i++) { print("bar"); } } } The same instance of FooBar will be passed to two different threads: thread A will call foo(), while thread B will call bar(). Modify the given program to output "foobar" n times.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: General problem-solving
1
2
print-in-order)print-zero-even-odd)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1115: Print FooBar Alternately
class FooBar {
private int n;
private Semaphore f = new Semaphore(1);
private Semaphore b = new Semaphore(0);
public FooBar(int n) {
this.n = n;
}
public void foo(Runnable printFoo) throws InterruptedException {
for (int i = 0; i < n; i++) {
f.acquire(1);
// printFoo.run() outputs "foo". Do not change or remove this line.
printFoo.run();
b.release(1);
}
}
public void bar(Runnable printBar) throws InterruptedException {
for (int i = 0; i < n; i++) {
b.acquire(1);
// printBar.run() outputs "bar". Do not change or remove this line.
printBar.run();
f.release(1);
}
}
}
// Accepted solution for LeetCode #1115: Print FooBar Alternately
// Auto-generated Go example from java.
func exampleSolution() {
}
// Reference (java):
// // Accepted solution for LeetCode #1115: Print FooBar Alternately
// class FooBar {
// private int n;
// private Semaphore f = new Semaphore(1);
// private Semaphore b = new Semaphore(0);
//
// public FooBar(int n) {
// this.n = n;
// }
//
// public void foo(Runnable printFoo) throws InterruptedException {
// for (int i = 0; i < n; i++) {
// f.acquire(1);
// // printFoo.run() outputs "foo". Do not change or remove this line.
// printFoo.run();
// b.release(1);
// }
// }
//
// public void bar(Runnable printBar) throws InterruptedException {
// for (int i = 0; i < n; i++) {
// b.acquire(1);
// // printBar.run() outputs "bar". Do not change or remove this line.
// printBar.run();
// f.release(1);
// }
// }
// }
# Accepted solution for LeetCode #1115: Print FooBar Alternately
from threading import Semaphore
class FooBar:
def __init__(self, n):
self.n = n
self.f = Semaphore(1)
self.b = Semaphore(0)
def foo(self, printFoo: "Callable[[], None]") -> None:
for _ in range(self.n):
self.f.acquire()
# printFoo() outputs "foo". Do not change or remove this line.
printFoo()
self.b.release()
def bar(self, printBar: "Callable[[], None]") -> None:
for _ in range(self.n):
self.b.acquire()
# printBar() outputs "bar". Do not change or remove this line.
printBar()
self.f.release()
// Accepted solution for LeetCode #1115: Print FooBar Alternately
use std::sync::Condvar;
use std::sync::Mutex;
struct FooBar {
flag: (Mutex<bool>, Condvar),
n: usize,
}
impl FooBar {
fn new(n: usize) -> Self {
let flag = (Mutex::new(true), Condvar::new());
FooBar { flag, n }
}
fn foo(&self, print_foo: impl Fn()) {
for _ in 0..self.n {
let (lock, cvar) = &self.flag;
let mut g = cvar
.wait_while(lock.lock().unwrap(), |flag| !*flag)
.unwrap();
*g = false;
print_foo();
cvar.notify_all();
}
}
fn bar(&self, print_bar: impl Fn()) {
for _ in 0..self.n {
let (lock, cvar) = &self.flag;
let mut g = cvar.wait_while(lock.lock().unwrap(), |flag| *flag).unwrap();
*g = true;
print_bar();
cvar.notify_all();
}
}
}
#[test]
fn test() {
use std::sync::mpsc::channel;
use std::sync::Arc;
use threadpool::ThreadPool;
let foo_bar = Arc::new(FooBar::new(4));
let (tx, rx) = channel();
let pool = ThreadPool::new(4);
{
let tx = tx.clone();
let foo_bar = foo_bar.clone();
pool.execute(move || {
foo_bar.foo(|| tx.send("foo".to_string()).unwrap());
});
}
{
let tx = tx;
let foo_bar = foo_bar;
pool.execute(move || {
foo_bar.bar(|| tx.send("bar".to_string()).unwrap());
});
}
pool.join();
let ss: Vec<String> = rx.try_iter().collect();
let res = ss.join("");
let ans = "foobarfoobarfoobarfoobar".to_string();
assert_eq!(ans, res);
}
// Accepted solution for LeetCode #1115: Print FooBar Alternately
// Auto-generated TypeScript example from java.
function exampleSolution(): void {
}
// Reference (java):
// // Accepted solution for LeetCode #1115: Print FooBar Alternately
// class FooBar {
// private int n;
// private Semaphore f = new Semaphore(1);
// private Semaphore b = new Semaphore(0);
//
// public FooBar(int n) {
// this.n = n;
// }
//
// public void foo(Runnable printFoo) throws InterruptedException {
// for (int i = 0; i < n; i++) {
// f.acquire(1);
// // printFoo.run() outputs "foo". Do not change or remove this line.
// printFoo.run();
// b.release(1);
// }
// }
//
// public void bar(Runnable printBar) throws InterruptedException {
// for (int i = 0; i < n; i++) {
// b.acquire(1);
// // printBar.run() outputs "bar". Do not change or remove this line.
// printBar.run();
// f.release(1);
// }
// }
// }
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.