A candidate who sat through Samsung R&D's coding round in Bangalore described getting one problem and three hours, no partial credit conversation, just a compiler that either accepted all fifty-plus hidden test cases or didn't. That single detail tells you most of what you need to know about Samsung's process: it rewards people who can grind through edge cases alone, not people who are good at narrating their thinking to a friendly panel. Glassdoor lists 284 separate software engineer interview question reports for Samsung Electronics alone, and reviewers rate the difficulty at 3.1 out of 5 with 60 percent describing the overall experience as positive (Glassdoor, Samsung Electronics Software Engineer interviews).
The process splits into two fairly different tracks depending on region and business unit. Samsung Research America and the US-based product teams run something closer to a standard big-tech loop: recruiter screen, a proctored online assessment, one or two live coding interviews, then an onsite or virtual loop that adds a hiring manager conversation and, for senior roles, a design discussion. Samsung R&D Institute India runs a stricter, higher-stakes gate. Multiple GeeksforGeeks writeups describe a pen-and-paper coding round with a single problem, no IDE, no autocomplete, followed by a technical round and then HR. Some reports mention the coding round difficulty climbing over the last few years, with interviewers leaning harder on graphs, backtracking, and dynamic programming instead of easier array manipulation.
What doesn't show up nearly enough in generic prep guides is how much of Samsung's technical round is old-fashioned computer science, operating systems, DBMS, computer networks, not framework trivia. Testing-focused roles get asked about STLC and bug report quality directly. This page groups roughly fifty candidate-reported questions into the rounds people actually described, with original answers written for someone prepping seriously, not skimming a list the night before.
Online assessment and coding round
Samsung's OA is usually one hard problem rather than four easy ones, and R&D India's pen-and-paper round removes the safety net of a compiler entirely. Graph traversal, DP, and greedy interval problems dominate the reports.
Easy questions
11Plain BFS from the start cell, since every move costs the same and BFS guarantees the first time you reach the target is via the shortest path. Push the start into a queue, mark it visited, and expand in all four directions, only enqueuing unvisited cells that are walkable.
from collections import deque
def shortest_path(grid, start, end):
rows, cols = len(grid), len(grid[0])
q = deque([(start, 0)])
visited = {start}
while q:
(r, c), dist = q.popleft()
if (r, c) == end:
return dist
for dr, dc in [(-1,0),(1,0),(0,-1),(0,1)]:
nr, nc = r + dr, c + dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1 and (nr, nc) not in visited:
visited.add((nr, nc))
q.append(((nr, nc), dist + 1))
return -1This one usually opens the round because it's a fair warm-up, but Samsung's own R&D reports mention it's still solved incorrectly often enough that interviewers keep it in rotation as a filter.
A virtual function has a default implementation in the base class that derived classes can override, but don't have to. A pure virtual function, declared with = 0, has no implementation in the base class at all, and any class containing one becomes abstract, meaning you can't instantiate it directly.
class Shape {
public:
virtual double area() = 0; // pure virtual, makes Shape abstract
virtual void describe() { std::cout << "A shapen"; } // virtual, has a default
};The practical consequence: forgetting to override a pure virtual function in a derived class is a compile error. Forgetting to override a regular virtual function just means you silently inherit the base behavior, which is a much easier bug to miss.
Encapsulation is bundling data with the methods that operate on it and hiding internal state, a BankAccount class exposing deposit() and withdraw() but keeping the balance field private so nothing outside can set it to an invalid value directly. Abstraction is exposing only what a caller needs, a database connection class that exposes query() without exposing the socket-level protocol underneath.
Inheritance is a derived class reusing and extending a base class, a SavingsAccount extending Account to add interest calculation. Polymorphism is calling the same method name on different objects and getting behavior specific to each one, calling draw() on a list of mixed Shape objects and getting a circle drawn for one and a square for another, resolved at runtime through virtual dispatch.
A process has its own independent memory space, its own copy of the code, data, heap, and stack, isolated from every other process by the OS. A thread lives inside a process and shares that process's memory space, code, and open file handles with every other thread in the same process, each thread just gets its own stack and register set.
Creating a process means the OS has to allocate a fresh address space and set up page tables from scratch, which is expensive. Creating a thread reuses the parent process's existing address space, so it's mostly just allocating a stack and a thread control block, an order of magnitude cheaper in most implementations.
Mutual exclusion (a resource can only be held by one process at a time), hold and wait (a process holding one resource can request another without releasing what it already has), no preemption (a resource can't be forcibly taken from a process), and circular wait (a cycle of processes each waiting on a resource held by the next).
All four have to hold simultaneously for deadlock to occur, so breaking any single one prevents it. Circular wait is usually the easiest to attack in practice: enforce a global ordering on resource acquisition, every process must request resources in the same fixed order, and a cycle becomes mathematically impossible to form.
First normal form requires every column to hold atomic, indivisible values, no comma-separated lists jammed into one field. Second normal form requires every non-key column to depend on the whole primary key, not just part of it, which only matters once you have a composite key. Third normal form requires every non-key column to depend directly on the key, not on another non-key column, removing transitive dependencies.
Each form fixes a specific update anomaly: 1NF fixes the problem of not being able to query or index a value buried inside a combined field. 2NF and 3NF both fix redundancy that would otherwise force you to update the same fact in multiple rows, if a customer's city is stored redundantly next to every order row instead of once in a customers table, changing that city means hunting down every order row instead of one row.
TCP is connection-oriented and guarantees ordered, reliable delivery through acknowledgments and retransmission, at the cost of extra overhead and latency whenever a packet needs resending. UDP is connectionless and fire-and-forget, no handshake, no acknowledgment, no retransmission, which means lower latency but no guarantee a packet arrives, or arrives in order.
A video call chooses UDP on purpose because a dropped frame from half a second ago is worthless by the time TCP would finish retransmitting it, better to skip it and keep the call moving in near-real-time than to freeze the whole stream waiting for one lost packet to be resent in order.
Pick something you can actually defend line by line, not necessarily the most impressive-sounding project on your resume. Start with the problem it solved and why an existing library or simpler approach wasn't sufficient, then walk through the core algorithm or architecture decision, then name at least one tradeoff you made on purpose and one thing you'd do differently if you rebuilt it today.
Interviewers use this question specifically to catch resume padding, a project description that was written by a team lead or copied from a project plan rather than something the candidate built hands-on falls apart the moment someone asks "why did you choose X over Y there" and the candidate can't answer beyond "that's what the team decided."
The weak version of this answer talks about Samsung being a large, well-known company. The stronger version names something specific: a product line, a technical challenge unique to Samsung's scale (manufacturing at the volume Samsung operates at surfaces engineering problems most companies never encounter), or a specific team's mission that maps to something you've actually worked on or care about. Generic answers are instantly recognizable to an interviewer who's heard the same phrasing forty times that quarter.
A genuinely useful preparation step is reading about a recent Samsung product launch or engineering blog post and forming an actual opinion about it, not just memorizing a fact to recite, because a good interviewer will ask a natural follow-up question and a memorized answer falls apart under one more layer of "why do you think that."
The answer that lands is specific and shows a real behavior change, not a disguised strength ("I work too hard") and not something so generic it could apply to anyone ("I'm a perfectionist"). A believable version names something true and small enough to be credible, tends to over-scope estimates on unfamiliar work, for instance, and then describes a concrete adjustment made because of it, started breaking tasks into smaller pieces and tracking actual time spent against the estimate to recalibrate over a few sprints.
Interviewers use this question to gauge self-awareness more than the weakness itself. Someone who can name a real limitation and describe a genuine, ongoing correction reads as far more trustworthy than someone who deflects with a humblebrag.
Resist the urge to read the resume back verbatim in chronological order. The stronger approach is a short narrative arc, what you were solving early on, what skill or interest pulled you toward the next role, and what you're specifically looking for now, tied together with a clear thread rather than a list of disconnected jobs and technologies.
This is also the moment to preemptively address anything unusual on the resume, a gap, a lateral move, a short stint, in one calm sentence rather than leaving it for the interviewer to ask about later in a more pointed way. Addressing it yourself, briefly and without over-explaining, reads as more confident than hoping it goes unnoticed.
Medium questions
25This is a shortest-path problem on a weighted grid, so it's really Dijkstra or a 0-1 BFS variant dressed up as a frog-and-lily-pad story. Build a graph where each cell is a node, edges only exist between two 1-cells, and the edge weight depends on direction, horizontal jumps cost one weight, vertical jumps cost another. Run Dijkstra from the start cell with a min-heap keyed on accumulated cost, and stop once you pop the destination.
The trap most candidates fall into is treating this as plain BFS because it looks like a maze problem. BFS only gives you the shortest path in unweighted graphs, unit-cost edges. The moment horizontal and vertical costs differ, you need a priority queue, otherwise you'll return a path with the fewest jumps instead of the cheapest one, which fails the exact test cases interviewers write specifically to catch that mistake.
Sort the array first, O(n log n), then fix one number and use a two-pointer sweep across the remaining sorted subarray to find pairs that sum to its negative. For each fixed index i, set left at i+1 and right at the end, move left up if the current sum is too small, move right down if it's too large, and record a match when the sum hits zero.
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size() - 2; i++) {
if (i > 0 && nums[i] == nums[i-1]) continue; // skip duplicate anchors
int left = i + 1, right = nums.size() - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) { /* record triplet */ left++; right--; }
else if (sum < 0) left++;
else right--;
}
}The duplicate-skipping line is what separates a correct answer from one that returns the same triplet five times. It's a small detail interviewers watch for specifically because it's easy to forget under time pressure.
Standard bottom-up DP: dp[0] = 0, and for every amount from 1 to target, dp[amount] = 1 + min(dp[amount - coin]) across all coins where amount >= coin. If dp[target] is still infinity at the end, no combination works.
int[] dp = new int[target + 1];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
for (int amount = 1; amount <= target; amount++) {
for (int coin : coins) {
if (coin <= amount && dp[amount - coin] != Integer.MAX_VALUE) {
dp[amount] = Math.min(dp[amount], dp[amount - coin] + 1);
}
}
}Greedy (always take the largest coin that fits) works for denomination sets like US currency where each coin is a "nice" multiple of the smaller ones, but fails for sets like {1, 3, 4} targeting 6: greedy picks 4 then 1 then 1, three coins, while the actual optimum is 3 + 3, two coins. Interviewers ask for this counterexample specifically to check you're not reciting greedy as a universal shortcut.
Find the length of the longest common subsequence (LCS) between the two strings first. Every character in each string that isn't part of that LCS has to be either deleted or matched by an insertion, so the answer is (len1 - lcs) deletions plus (len2 - lcs) insertions.
def min_ops(s1, s2):
m, n = len(s1), len(s2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
if s1[i-1] == s2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])
lcs = dp[m][n]
return (m - lcs) + (n - lcs)It's worth saying out loud in the interview that this reduces cleanly to LCS, rather than trying to invent a new DP from scratch. Reusing a known building block is exactly the signal interviewers want from a mid-level candidate under time pressure.
Backtracking here means building the string one character at a time, tracking open-parens and close-parens used so far, and only recursing into a branch if it can still produce a valid final string. Add an open paren whenever open count is less than n. Add a close paren only when close count is less than open count, otherwise you'd produce an invalid prefix like "))".
def generate(n):
result = []
def backtrack(s, open_c, close_c):
if len(s) == 2 * n:
result.append(s)
return
if open_c < n:
backtrack(s + "(", open_c + 1, close_c)
if close_c < open_c:
backtrack(s + ")", open_c, close_c + 1)
backtrack("", 0, 0)
return resultThe pruning condition, close_c < open_c, is the whole trick. Without it you'd generate every string of balanced-length parens and filter afterward, which technically works but wastes exponentially more time than pruning as you go.
Combine a hash map with a doubly linked list. The hash map gives O(1) lookup from key to a node in the list, and the doubly linked list gives O(1) removal and re-insertion at the front (most recently used end) without shifting anything else, which a plain array or vector-based cache can't do.
class Node:
def __init__(self, key, val):
self.key, self.val = key, val
self.prev = self.next = None
class LRUCache:
def __init__(self, capacity):
self.cap = capacity
self.map = {}
self.head = Node(0, 0)
self.tail = Node(0, 0)
self.head.next = self.tail
self.tail.prev = self.head
def _remove(self, node):
node.prev.next, node.next.prev = node.next, node.prev
def _add_front(self, node):
node.next = self.head.next
node.prev = self.head
self.head.next.prev = node
self.head.next = node
def get(self, key):
if key not in self.map:
return -1
node = self.map[key]
self._remove(node)
self._add_front(node)
return node.val
def put(self, key, val):
if key in self.map:
self._remove(self.map[key])
node = Node(key, val)
self.map[key] = node
self._add_front(node)
if len(self.map) > self.cap:
lru = self.tail.prev
self._remove(lru)
del self.map[lru.key]The part candidates skip under pressure is the eviction step, removing the least-recently-used node from both the list and the map at the same time. Miss the map deletion and you get a memory leak that never shows up in a quick manual test but breaks a correctness test suite with enough operations.
The diamond problem happens when a class D inherits from two classes B and C, which both inherit from a common base A. Without any special handling, D ends up with two separate copies of A's members, one through B and one through C, which is ambiguous the moment you try to access anything from A through D.
class A { public: int x; };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {}; // only one copy of A nowC++ resolves it with virtual inheritance: declaring B and C's inheritance from A as virtual tells the compiler to share a single instance of A across the whole hierarchy instead of duplicating it. The cost is a small amount of extra indirection at runtime and a slightly more complex constructor initialization order, which is exactly why virtual inheritance is something you reach for deliberately, not by default on every base class.
Single responsibility (a class should have one reason to change), open-closed (open for extension, closed for modification), Liskov substitution (a subclass should be usable anywhere its base class is expected without breaking behavior), interface segregation (don't force a class to implement methods it doesn't need), and dependency inversion (depend on abstractions, not concrete implementations).
The interviewer isn't grading whether you can recite all five in order, they're checking whether you can name a real violation. A common honest answer: a UserService class that both validated input and sent emails, single-responsibility violation, split apart once email logic needed to change independently of validation logic and every unrelated change to one kept breaking tests for the other.
A mutex is a locking mechanism tied to ownership: whichever thread locks it is the only thread allowed to unlock it, and it's meant strictly for mutual exclusion, one thread in a critical section at a time. A semaphore is a counter with no ownership concept at all, any thread can increment (signal) or decrement (wait) it, and it's meant for signaling between threads as much as for exclusion, a counting semaphore initialized to N lets up to N threads into a section concurrently, not just one.
The ownership distinction is what actually trips candidates up. Because a mutex has an owner, most implementations will flag or forbid a different thread unlocking it than the one that locked it. A semaphore has no such restriction, which makes it flexible enough for producer-consumer signaling but also easier to misuse if you're not careful about which thread is expected to post versus wait.
Two producers and consumers share a fixed-size buffer. The producer has to block when the buffer is full, the consumer has to block when it's empty, and both need mutual exclusion while actually touching the buffer, otherwise you can get a lost update or a read of a half-written slot.
sem_t empty, full, mutex;
sem_init(&empty, 0, BUFFER_SIZE); // slots available
sem_init(&full, 0, 0); // items available
sem_init(&mutex, 0, 1); // binary lock
void producer(item) {
sem_wait(&empty); // block if buffer full
sem_wait(&mutex);
add_to_buffer(item);
sem_post(&mutex);
sem_post(&full); // signal one more item available
}
void consumer() {
sem_wait(&full); // block if buffer empty
sem_wait(&mutex);
item = remove_from_buffer();
sem_post(&mutex);
sem_post(&empty); // signal one more slot free
}The empty and full counting semaphores are what remove the busy-wait: instead of the producer looping and checking "is the buffer full?" repeatedly, sem_wait(&empty) puts it to sleep on the OS scheduler's queue until a consumer's sem_post(&empty) wakes it, no wasted CPU cycles spinning.
LRU evicts the page that hasn't been accessed for the longest time, on the theory that recent access patterns predict near-future ones, a page you just used is likely to be used again soon. FIFO evicts whichever page was loaded first, regardless of how recently it was actually used, which can throw out a page that's still actively being referenced just because it happened to arrive early.
FIFO also has a known pathology called Belady's anomaly, where increasing the number of available page frames can actually increase the number of page faults, which is deeply counterintuitive and one of the reasons LRU (or its cheaper approximations like clock/second-chance) is preferred in practice despite being more expensive to implement exactly, since true LRU needs a way to track access recency for every page.
Contiguous allocation gives each process one unbroken block of physical memory. It's simple and fast to access, but leads to external fragmentation: over time, free memory ends up scattered in small chunks between allocated blocks, none of them individually big enough for a new process even though the total free memory is plenty.
Paging solves this by breaking both physical memory and each process's address space into fixed-size chunks, frames and pages, and letting a process's pages live in any available frames, not necessarily adjacent ones. A page table maps each virtual page to whatever physical frame it actually landed in. The cost is an extra layer of indirection on every memory access and internal fragmentation instead, wasted space inside the last partially-used page, but that waste is bounded and small, unlike the unbounded external fragmentation contiguous allocation can accumulate.
Round robin gives every process a fixed time quantum in rotation, which guarantees fairness and bounded waiting time regardless of process priority, no process starves. Priority scheduling always runs the highest-priority ready process next, which can serve important, latency-sensitive work faster but risks starving low-priority processes indefinitely if higher-priority work keeps arriving.
Round robin performs worse specifically in workloads where a small quantum causes excessive context-switch overhead relative to actual work done per slice, lots of short-lived, latency-tolerant background jobs interleaved with one CPU-bound task that keeps getting interrupted before making meaningful progress. In that scenario, priority scheduling that lets the CPU-bound task run to completion or to a natural yield point finishes faster overall, even though round robin is "fairer" by the letter of the definition.
A B-tree index keeps keys sorted and balanced across levels, so a lookup, insert, or range scan all cost O(log n) disk reads, and because the keys are ordered, range queries (WHERE age BETWEEN 20 AND 30) can walk the tree efficiently instead of scanning the whole table. A hash index maps a key straight to a bucket via a hash function, giving O(1) average lookup for exact-match equality, but it has no ordering at all, so it can't help with a range query or an ORDER BY.
Reach for a hash index specifically when the workload is dominated by exact-match lookups on a column that never gets range-queried or sorted on, a session token or a UUID primary key looked up one at a time. Reach for B-tree, which is the default in most relational databases, for everything else, since it handles both exact match and range queries reasonably well even if neither is as fast as the specialized alternative.
Atomicity guarantees a transaction either fully completes or fully rolls back, preventing a bank transfer from debiting one account and crashing before crediting the other. Consistency guarantees a transaction moves the database from one valid state to another, respecting constraints, preventing a foreign key from pointing at a row that got deleted mid-transaction. Isolation guarantees concurrent transactions don't see each other's uncommitted intermediate state, preventing a read that picks up a half-finished update from a different transaction running at the same time. Durability guarantees a committed transaction survives a crash immediately after, preventing a confirmed order from silently disappearing if the server loses power a second later.
An inner join returns only rows where the join condition matches in both tables, orders that actually have a matching customer row. A left join returns every row from the left table, plus matching rows from the right table where they exist, and NULLs in the right table's columns where they don't, every customer whether or not they've placed an order yet. A full outer join returns every row from both tables, matched where possible, NULL-padded on whichever side has no match, every customer and every order, including customers with no orders and orphaned orders with no valid customer.
SELECT c.name, o.order_id
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id;
-- includes every customer, order_id is NULL for customers with no ordersThe client sends a SYN with an initial sequence number. The server responds with a SYN-ACK, acknowledging the client's sequence number and sending its own. The client sends a final ACK acknowledging the server's sequence number, and the connection is now established on both sides.
If that final ACK is lost, the server is still waiting in a SYN-RECEIVED state, since from its perspective the handshake never fully closed. The server's TCP stack will retransmit its SYN-ACK after a timeout, expecting another ACK. If the client had already started sending data (some implementations allow this optimistically), the server will actually accept that data as implicit acknowledgment that the connection succeeded, but the strict three-way handshake diagram assumes the ACK itself needs to arrive before either side proceeds.
DNS resolution first, the browser (or OS resolver) checks its cache, then queries a recursive resolver, which walks the DNS hierarchy, root, TLD, authoritative nameserver, to resolve the hostname to an IP address. Once the IP is known, the client opens a TCP connection to that IP on port 443 (or 80), which means the three-way handshake, SYN, SYN-ACK, ACK.
For HTTPS, a TLS handshake follows the TCP handshake: the client and server negotiate a cipher suite, the server presents its certificate, and both sides derive a shared session key, after which application data is encrypted. Only once all of that completes does the browser send its actual HTTP GET request, and the server's response starts flowing back over the now-established encrypted connection.
Write-through writes to the cache and the underlying store at the same time, synchronously, so the cache and the source of truth never diverge, at the cost of every write being as slow as the slowest of the two. Write-back writes to the cache immediately and returns, then flushes to the underlying store asynchronously later, which is much faster for the caller but risks losing that write entirely if the cache crashes before the flush happens.
For something like device settings sync, where losing a recent change on a crash is a real, visible problem for the user (their toggle silently reverts), write-through is the safer default even though it's slower, because correctness matters more than shaving milliseconds off a settings save that happens rarely. Write-back earns its keep in high write-volume scenarios like a metrics counter, where losing the last few increments on a rare crash is an acceptable tradeoff for much higher throughput the rest of the time.
The honest answer format that actually holds up under follow-up questions: name the constraint precisely (a deadline, a resource limit, an ambiguous requirement, a bug that only reproduced intermittently), explain the first approach you tried and specifically why it didn't work, then explain what you changed and why that fixed it. Vague answers like "we had a tight deadline and I worked hard" collapse the moment an interviewer asks "what was the actual blocker."
A concrete shape that reads well: a memory leak in a long-running service that only showed up after days of uptime, not in any test environment because tests never ran that long. The obstacle wasn't finding the leak once you had the right tool, it was realizing standard test suites couldn't reproduce it at all, and the fix was building a long-running soak test into the CI pipeline specifically to catch this class of bug earlier next time. That kind of answer demonstrates you learned something durable from the incident, not just that you fixed one bug.
Profile before touching anything, guessing at the bottleneck in pointer-heavy C or C++ code is a losing game because cache behavior and memory access patterns matter as much as raw instruction count, and intuition about "which part is slow" is wrong more often than people expect. Once the profiler points at a specific hot path, look first at memory access patterns, cache-unfriendly pointer chasing (following a linked list scattered across the heap, for instance) is a common, high-impact culprit that a simple algorithmic complexity analysis wouldn't catch at all.
Common concrete fixes once you've found the actual hot path: replacing scattered pointer-based structures with contiguous, cache-friendly layouts where access patterns allow it, eliminating unnecessary copies by passing by reference or pointer instead of by value, and reducing redundant memory allocations inside a hot loop by reusing a buffer instead of allocating fresh memory on every iteration. Measure again after each change individually, since stacking multiple changes at once makes it impossible to tell which one actually helped.
Functional test cases verify the feature does what it's supposed to do under normal, expected conditions. Boundary and edge-case tests target the extremes, empty input, maximum length, zero, negative numbers, off-by-one boundaries, where bugs cluster disproportionately compared to how much of the input space they represent. Negative tests verify the system fails gracefully on invalid input rather than crashing or corrupting state. Regression tests verify a fix or new feature didn't break something that used to work.
Deciding "enough" coverage isn't really a percentage question, it's a risk question: which failure modes would actually hurt a real user or violate a real requirement, and are those specifically covered, rather than chasing a coverage number that includes a lot of low-risk paths tested repeatedly while a genuinely risky edge case goes untested. A team that treats 90 percent code coverage as the goal can still ship a bug that a single well-chosen boundary test would have caught.
Structure it around the actual decision you made under pressure, not just the stress itself. What got cut or deferred, what you communicated to whom and when, and what the actual outcome was, delivered on time with a smaller scope, delivered late but communicated early enough that it wasn't a surprise, or something in between. Interviewers are listening for whether you make deliberate tradeoffs under pressure or just work more hours and hope it's enough.
The detail that separates a strong answer from an average one is naming what you explicitly chose not to do and why, cut a nice-to-have feature, skipped a manual test pass in favor of an automated one you trusted more, because that shows judgment under constraint rather than just endurance.
Pick a real failure with real consequences, not a disguised success story dressed up as a failure. A genuine answer names what specifically went wrong, whether it was a wrong technical assumption, a missed communication, or a decision made with incomplete information, and separates that from what you did afterward to make sure the same category of mistake wouldn't repeat.
The most common way this answer goes wrong is ending at "and I learned to be more careful," which is too vague to be believable. A stronger close names a specific process or habit change that resulted directly from the failure, added a peer-review step for a certain type of change, started writing a design doc before starting a certain size of project, something concrete enough that an interviewer could picture you actually doing it today.
The answer that works shows you can hold a technical position with evidence while still being genuinely open to being wrong, not just performing agreeableness and not digging in for the sake of being right. Name the actual disagreement concretely, a caching strategy, a library choice, a timeline estimate, what evidence you brought to the conversation, and how the resolution actually happened, did new information change your mind, did you defer because it wasn't worth the fight, did you escalate because the stakes justified it.
Avoid two failure modes here: a story where you were simply right and the other person simply capitulated, which reads as one-sided and unlikely to be the whole truth, and a story where you just deferred immediately without a real technical conversation, which reads as conflict-avoidant rather than collaborative.
Hard questions
7Treat this as a constrained optimization over a small, fixed set of choices, five paces, applied across the distance. Because you're choosing a pace for each unit or segment of distance and want to minimize total time subject to a total energy budget, this is a variant of the knapsack pattern: state is (distance covered so far, energy spent so far), and you're minimizing time.
Define dp[e] as the maximum distance achievable using exactly e units of energy, iterating paces like knapsack items. For each pace with energy cost c and speed s, distance gained per unit energy is s/c, so dp[e] = max(dp[e], dp[e-c] + s) for each pace. Once you know the max distance reachable at every energy level up to the budget, walk backward to find the minimum total time across combinations that cover at least the required distance. The five-pace constraint is a strong hint this is meant to be solved with a small DP table, not a greedy pick of the fastest pace, because fastest isn't always most energy-efficient per unit distance.
This is shortest path on a graph where most edges cost one move (up, down, left, right) but wormhole edges cost zero, which makes it a 0-1 BFS problem rather than a job for plain Dijkstra or plain BFS. Model each wormhole pair as a zero-cost edge and every regular adjacent move as a cost-one edge, then use a deque: push zero-cost transitions to the front, one-cost transitions to the back, and pop from the front each time.
Candidates who reach straight for Dijkstra with a priority queue will still get a correct answer, since Dijkstra handles this fine, but it's O(E log V) instead of the O(V + E) you get from 0-1 BFS, and on a grid with a lot of wormholes, that difference is exactly the kind of thing a follow-up question probes: "can you do better than the heap?"
Sort the hole positions first. Then greedily place a patch so its left edge starts exactly at the leftmost uncovered hole, which covers everything up to (position + k - 1). Advance to the next hole that falls outside the current patch's coverage and repeat. This is the classic interval-covering greedy pattern, and it works here because every patch has identical fixed length, so the only decision that matters is where the left edge starts, not which size patch to use.
def min_patches(holes, k):
holes.sort()
patches = 0
i = 0
n = len(holes)
while i < n:
end = holes[i] + k - 1
patches += 1
while i < n and holes[i] <= end:
i += 1
return patchesThe interviewer's real question here is whether you recognize this as interval scheduling rather than trying to brute-force combinations of patch placements, which blows up quickly once the hole count grows past a dozen or so.
With a two-pan balance, each weight can go on the same side as the item, the opposite side, or be unused, which gives each weight three effective states instead of the usual two in a subset-sum problem. Work out every value reachable as a signed sum of a subset of the weights (each weight contributing +w, -w, or 0), collect those into a set, then check whether every integer from A to B appears in that reachable set.
If there's a gap, the missing value (or the smallest gap) tells you roughly what additional weight to add. This is really a reachability problem over ternary choices rather than the binary include-or-exclude of standard subset sum, and candidates who default to a plain 0/1 subset-sum DP will get an incomplete reachable set because they're missing the "put it on the opposite side" case entirely.
The standard states are new, ready, running, blocked (waiting), and terminated. New to ready happens once the OS finishes admission and allocates initial resources. Ready to running happens when the scheduler dispatches the process onto a CPU. Running to ready happens on a timer interrupt or preemption, the process is still able to run, it just lost its CPU slot. Running to blocked happens when the process requests something it has to wait for, an I/O operation, a lock it can't acquire yet. Blocked to ready happens once whatever it was waiting on becomes available, the I/O completes, the lock is released.
The distinction candidates blur most often: ready means "able to run right now, just not scheduled," while blocked means "can't run even if scheduled, because it's waiting on an external event." A process can only go straight from running to ready or running to blocked, never directly from blocked to running, it always has to pass through ready first so the scheduler gets a chance to pick the next process fairly.
Both are the same underlying condition, a cycle of processes or transactions each waiting on a resource held by another. The difference is in resources and detection strategy. An OS deadlock typically involves locks on memory, devices, or file handles, and many OS designs favor deadlock prevention, enforcing a strict resource-acquisition order, over detection, because recovering from an OS-level deadlock by killing a process can be disruptive.
A database deadlock involves row or table locks held by concurrent transactions, and most relational databases favor active detection over prevention: they periodically build a wait-for graph among active transactions and, on finding a cycle, pick one transaction as the "victim," abort it, roll back its changes, and let the others proceed. That's a deliberate design choice, transactions are already built to be rolled back safely, so detection-and-kill is cheap and reliable in a way it usually isn't for an arbitrary OS process holding an arbitrary resource.
STLC covers requirement analysis (understanding what needs testing before writing a single test case), test planning (scope, resources, schedule), test case design, test environment setup, test execution, and closure/reporting. It runs alongside the development lifecycle rather than only starting once code is complete, requirement analysis in particular should happen as early as the requirements themselves are written, so ambiguities get caught before they become bugs.
An effective bug report needs enough for someone who wasn't there to reproduce it without asking follow-up questions: exact steps to reproduce, expected behavior versus actual behavior, environment details (OS version, build number, device model for hardware-adjacent work), severity and reproducibility rate (does it happen every time or intermittently), and supporting evidence, a screenshot, log snippet, or stack trace. A report that just says "the app crashed" with no repro steps wastes more engineering time than the bug itself usually costs to fix.
Real-time scenario questions
7Start from the entities a real parking lot actually has: ParkingLot (owns a collection of levels), Level (owns spots), Spot (has a type: compact, large, handicapped, and a status: free or occupied), Vehicle (with a subtype hierarchy for car, motorcycle, bus), and a Ticket that gets issued on entry and closed on exit.
class Spot {
public:
SpotType type;
bool occupied = false;
};
class ParkingLot {
public:
std::vector<Level> levels;
Ticket* parkVehicle(Vehicle* v) {
for (auto& level : levels) {
if (Spot* s = level.findAvailableSpot(v->size)) {
s->occupied = true;
return new Ticket(v, s);
}
}
return nullptr; // lot full
}
};The follow-up almost always tests whether you can extend the design cleanly: adding a pricing strategy that charges differently by vehicle type and duration, without touching the Spot or Level classes at all. That's the actual signal, spot-finding logic is table stakes, a design that survives a new requirement without a rewrite is the real bar.
Start with a queue-based, fan-out architecture rather than a synchronous send-and-wait call for every notification. An API accepts a notification request and drops it onto a message queue immediately, returning success to the caller without waiting for actual delivery. Separate worker pools per channel, push, SMS, email, consume from the queue and call the relevant third-party gateway (APNs, FCM, an SMS provider, an email service).
Retries need exponential backoff and a dead-letter queue: a failed send goes back onto a delayed retry queue instead of blocking the main pipeline, and after a fixed number of attempts it moves to a dead-letter queue for manual inspection instead of retrying forever. The part interviewers actually probe on is deduplication and ordering, if a retry and the original send both eventually succeed, does the user get the notification twice, and what's your idempotency key strategy to prevent that.
The core operation is generating a short, unique code for a long URL and storing the mapping, then redirecting on lookup. Generation can use a counter-based approach, base-62 encode an auto-incrementing ID, which guarantees uniqueness without coordination, or a hash-based approach with collision checking, which is simpler to shard but needs a retry loop on collision.
The bottleneck at real scale isn't writes, shortening happens far less often than clicking, it's reads: a popular shortened link can get hammered with redirect requests. That pushes the design toward a heavy caching layer in front of the database, most short-code lookups should never touch the database at all, and toward keeping the redirect service itself as close to stateless as possible so it can scale horizontally behind a load balancer without any shared session state.
This is fundamentally a publish-subscribe problem rather than a direct device-to-device call model, because the number of possible sender-to-receiver pairings grows fast as more device types get added, and you don't want every device to need explicit knowledge of every other device it might need to talk to. A lightweight message broker, MQTT is the common real-world choice for exactly this kind of constrained, low-bandwidth device network, sits in the middle. Devices publish state changes to a topic, other devices subscribe to the topics they care about, and the broker handles fan-out.
The two things that actually matter in the design discussion: local-first reliability, if the internet connection to a cloud broker drops, devices on the same local network should still be able to react to each other without round-tripping to the cloud, which usually means a local broker or hub that syncs to the cloud opportunistically rather than being a hard dependency. And message ordering guarantees, a light shouldn't act on a stale "door open" event that arrives after a newer "door closed" event because of network jitter, which pushes toward including a timestamp or sequence number in every published message so subscribers can discard out-of-order ones.
Start by reproducing the failure rate and conditions before touching any code, check logs and metrics for correlation with load, time of day, specific input patterns, deploy timing, because "intermittent" almost always means there's a hidden variable, a race condition, a resource exhaustion threshold, a specific edge-case input, not true randomness. Bisect if it's recent, correlate the first occurrence against the deploy history to narrow down whether a specific change introduced it.
Once there's a working hypothesis, isolate it in a lower environment where you can safely reproduce and instrument more aggressively than you'd want to in production, extra logging, a debugger, a stress test that amplifies whatever condition you suspect. The instinct to avoid is guessing and shipping a fix without first confirming the actual root cause, since a plausible-looking fix for the wrong cause just makes the bug harder to find the second time it resurfaces.
Each device ships logs to a lightweight local agent that batches and compresses them before sending, rather than making a network call per log line, which would overwhelm both the device and the ingestion layer. Ingestion sits behind a durable, ordered log itself, a Kafka-style pipeline, so a burst from thousands of devices doesn't overwhelm downstream storage directly, and consumers can process at their own pace.
From the ingestion pipeline, logs get written to a storage tier optimized for both write throughput and search, commonly a combination of cheap object storage for the raw logs and a search index (Elasticsearch-style) holding parsed, indexed fields for the fast-query path. Retention at 30 days is handled with time-based partitioning, logs are written into day-keyed partitions, and a scheduled job simply drops partitions older than 30 days rather than running expensive row-by-row deletes.
A sliding-window counter is usually the right tradeoff between accuracy and cost. A fixed window (reset the counter every 60 seconds) is simple but lets a user burst up to double the limit right at the window boundary, near-max requests at the tail of one window and near-max again at the start of the next. A sliding window log, storing every request timestamp, is perfectly accurate but memory-heavy at scale. A sliding window counter approximates the accurate answer by weighting the previous window's count proportionally to how much of it overlaps the current sliding window, cheap to compute and close enough in practice.
def is_allowed(user_id, current_window_count, prev_window_count, elapsed_in_window, window_size, limit):
weighted_count = prev_window_count * (1 - elapsed_in_window / window_size) + current_window_count
return weighted_count < limitAt scale this state lives in a fast, shared store like Redis, with each check being a single atomic increment-and-compare, and distributed rate limiting across multiple API servers has to share that same store rather than tracking counts locally per server, otherwise a user could get 100 requests per server instead of 100 total.
How to prepare for a Samsung interview
Don't split prep evenly across everything on this page. If you're interviewing for an R&D India role, weight your time toward the pen-and-paper coding round specifically, practicing DSA problems without an IDE or autocomplete changes what "practice" even means, you're training your ability to spot bugs by reading code, not by running it. If you're interviewing with a US-based team, the OS and DBMS fundamentals sections matter less than being ready to defend a system design decision with actual tradeoffs, since those loops lean more architecture-and-experience than raw algorithm speed.
Across mock interviews run through LastRoundAI, candidates prepping for hardware or manufacturing-adjacent companies consistently underprepare the operating systems and testing sections relative to how often they actually come up, most study time goes to LeetCode-style DSA because that's what most generic prep content emphasizes, and the STLC or memory-management questions catch people flat-footed as a result. That's an observation from what we see in practice sessions, not a precise statistic, but it's consistent enough across candidates prepping for this kind of role to be worth calling out directly.
Get the reps in before the real thing
Reading through fifty questions is not the same as defending an answer out loud once an interviewer asks a pointed follow-up. LastRoundAI's mock interview mode runs live coding and behavioral rounds with real-time follow-ups in your browser, and the free plan includes 15 credits a month that reset monthly rather than piling up unused. Starter is $19/mo if a handful of sessions isn't enough runway before an onsite.
Once your answers hold up under a follow-up, the slower part is usually getting in front of enough Samsung roles that actually match your background instead of blindly applying to everything open. Auto-Apply queues tailored applications for your review, 10 a month on the free plan, up to 400 a month on the Ultimate plan, and nothing goes out until you approve it.
Questions about either product go to contact@lastroundai.com. That's the only inbox we check.
How this list was built
Worth being straight about where these questions come from, because plenty of pages in this category are not. The set was compiled from a research pass across official documentation, vendor release notes, published engineering writing and public discussion of hiring processes, then cross-checked against the current version of each technology so nothing here describes behaviour that has since changed.
What that means in practice: these are the questions the material supports as reasonable and current for this role, not a transcript of any one company's loop. We have not sat in on your interview and we are not going to claim we have. Treat the list as well-sourced preparation rather than a leaked question bank, and expect your panel to phrase things their own way.
If you spot something out of date, tell us at contact@lastroundai.com and we will fix it.
Frequently asked questions
How many interview rounds does Samsung have?
Usually a recruiter screen, a technical phone screen, then an onsite loop of four to five rounds covering coding, system design and behavioural. The exact count shifts by level and team, and some loops add a domain-specific round, so ask your recruiter for the actual schedule.
How hard is the Samsung interview?
Hard, but the difficulty is more about depth of follow-up than exotic questions. Interviewers tend to take a reasonable problem and keep pushing on trade-offs, edge cases and what you would change at ten times the load. Preparing to be interrogated on an answer matters more than memorising more answers.
What coding level should I prepare for a Samsung interview?
Comfortable medium-difficulty problems solved cleanly while talking, rather than hard problems solved silently. Most rejections come from unclear communication and untested edge cases, not from failing to find an exotic algorithm.
Does Samsung ask system design at every level?
From mid-level upward, almost always, and it carries increasing weight as you go up. Junior loops may include a lighter version focused on structuring a feature rather than designing a distributed system.
- Samsung Electronics Software Engineer Interviews
- Samsung R&D Institute India Bangalore Interviews
- Samsung RnD Coding Round Questions, GeeksforGeeks
- Samsung R&D Interview Experience, SDE-1, GeeksforGeeks
- Samsung's Interview Process (2026), TechPrep
- Samsung Interview Questions, Technical and HR Round Guide, FaceGraph
