Multiline Text Editor with Cursor Movement
Reported by candidates from DatologyAI's online assessment. Pattern, common pitfall, and the honest play if you blank under the timer.
The DatologyAI OA reported in September 2026 isn't a hard algorithm problem. It's a spec-reading problem. You build a multiline text editor with a cursor, and the input is tiny: at most 100 operations, at most 20 inserted letters each, at most 20 prints. That means brute force is fine, and nobody is grading you on cleverness. They're grading whether you handle every boundary case without a bug. If you blank on the line-joining logic, StealthCoder can run as a safety net during the live OA. Know the shape first.
The problem
Implement a text editor that begins with one empty line and a cursor at column 0 of that line. A cursor is a gap between characters: column 0 is before the first character and column equal to the line length is after the last. Process operations in order. Each row is one of the following: ["INSERT", text]: insert the lowercase letters in text at the cursor, without overwriting existing characters. The cursor ends immediately after the inserted text. ["BACKSPACE"]: remove the character immediately before the cursor. At the beginning of a non-first line, remove the preceding newline instead, joining this line onto the previous line and leaving the cursor at the join. At the beginning of the whole document, do nothing. ["NEWLINE"]: split the current line at the cursor. The suffix becomes a new next line, and the cursor moves to column 0 of that new line. ["LEFT"] or ["RIGHT"]: move one character gap horizontally. Moving left from a line's beginning crosses the preceding newline to the previous line's end. Moving right from a line's end crosses the following newline to the next line's beginning. At the document's outer boundaries, do nothing. ["UP"] or ["DOWN"]: move to the adjacent line, keeping the current column if it exists there; otherwise use that line's end. If there is no adjacent line in that direction, do nothing. Each move uses the current column: there is no remembered preferred column after a shorter line clamps it. ["PRINT"]: record an independent snapshot of every line in document order, including empty lines. Printing does not move the cursor. Return a String[][] containing one row per PRINT. Each such row contains the strings for all lines at that moment; line separators are represented by separate strings, not embedded newline characters. The empty document prints as [""]. Return an empty outer array if there are no PRINT operations. The cursor is not included in the output. Function runCursorEditor(operations: String[][]) → String[][] Examples Example 1 operations = [["INSERT","abc"],["LEFT"],["NEWLINE"],["INSERT","x"],["PRINT"],["UP"],["INSERT","z"],["PRINT"]] return = [["ab","xc"],["azb","xc"]] After inserting abc, moving left and splitting, the lines are ab and c. Inserting x produces xc with the cursor at column one. UP moves to column one of ab, so inserting z makes azb. The first snapshot remains unchanged. Example 2 operations = [["INSERT","abcd"],["NEWLINE"],["INSERT","x"],["NEWLINE"],["INSERT","wxyz"],["UP"],["UP"],["INSERT","q"],["PRINT"]] return = [["aqbcd","x","wxyz"]] The cursor starts the two UP moves at column four of wxyz. Moving to the one-letter middle line clamps it to column one. Moving up again keeps that new column one, not the old column four. Inserting q therefore produces aqbcd. Constraints 0 ≤ operations.length ≤ 100. Every row is a valid operation with the stated number of fields. Each INSERT text contains from 1 through 20 lowercase English letters and no newline. NEWLINE is the only way to create a line separator. There are at most 20 PRINT operations. Movement and backspace at boundaries are valid no-ops as specified.
Reported by candidates. Source: FastPrep
Pattern and pitfall
The pattern is plain simulation. Keep a list of strings for lines, plus a row and column for the cursor. Every operation is a small string splice. INSERT slices the line at the column and rebuilds it. BACKSPACE at column 0 on a non-first line concatenates the line onto the previous one and sets the column to the old previous length. NEWLINE splits the line and inserts the suffix as a new line. The classic pitfall is UP and DOWN. The spec says there is no remembered preferred column, so clamp the column and keep the clamped value. Example 2 tests exactly this. The other trap is PRINT, which must copy the lines, not store a reference. With input this small, O(n * L) per operation is fine. StealthCoder is the hedge if the live OA throws off your indexing, but the logic is short.
If you see this problem in your OA tomorrow, the play is to recognize the pattern in 30 seconds. StealthCoder buys you that recognition.
You can drill Multiline Text Editor with Cursor Movement cold, or you can hedge it. StealthCoder runs invisibly during screen share and surfaces a working solution in under 2 seconds. The proctor sees the IDE. They don't see what's behind it. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken.
Get StealthCoderRelated leaked OAs
You've seen the question.
Make sure you actually pass DatologyAI's OA.
DatologyAI reuses patterns across OAs. Built by an Amazon engineer who passed his OA cold and still thinks the filter is broken. Works on HackerRank, CodeSignal, CoderPad, and Karat.
Multiline Text Editor with Cursor Movement FAQ
How hard is the DatologyAI cursor editor problem really?+
Easy on algorithms, medium on care. There's no data structure trick. The input is at most 100 operations, so rebuilding strings is fine. Most failures come from off-by-one errors at line boundaries, not from the approach. Read each operation's boundary rule twice before coding.
What's the trick to UP and DOWN?+
Clamp the column to the target line's length and keep that clamped value. Don't store a preferred column. Example 2 shows it: moving up from column four to a one-letter line gives column one, and the next UP stays at column one. Remembering the old column gives the wrong answer.
How should I store the document?+
A list of strings, one per line, plus cursor row and column. Don't use one big string with embedded newlines. The output wants separate strings per line anyway. Strings are immutable in most languages, so rebuild the current line on each edit. At this input size, that's fast enough.
What bugs break the PRINT operation?+
Aliasing. If you add your live line list to the result without copying, later edits change earlier snapshots. Example 1 checks this because the first snapshot must stay unchanged. Copy the list on every PRINT. Also remember an empty document prints as a list with one empty string.
How do I prepare for this in 48 hours?+
Write the editor once from scratch and run both examples by hand. Then test the edge cases: BACKSPACE at the document start, LEFT at the first line start, RIGHT at the last line end, and BACKSPACE joining two lines. Check where the cursor lands after the join. Under an hour is enough.