AlgoMaster Logo

Design a Text Editor

hardFrequencyUpdated September 21, 2026

Understanding the Problem

We need to simulate a text editor where all operations happen relative to a cursor position. The cursor splits the text into two halves: everything to its left and everything to its right. Every operation either modifies characters near the cursor or moves the cursor itself.

All operations are local to the cursor. addText inserts at the cursor, deleteText removes characters immediately to its left, and cursorLeft and cursorRight shift its position. Nothing ever reads or modifies characters far from the cursor. This locality drives the optimal solution. The question becomes: what data structure supports efficient insertion, deletion, and access right next to one moving position?

Key Constraints:

  • 1 <= text.length, k <= 40 -> Each individual operation involves at most 40 characters, so per-character work within one operation is cheap.
  • At most 2 * 10^4 total calls -> The text can grow to about 8 * 10^5 characters. An O(n) per operation approach still passes at this size, but an approach that touches only characters near the cursor costs O(k) per operation, effectively constant since k <= 40.
  • text consists of lowercase English letters -> Single-byte characters with no case or encoding edge cases.

Approach 1: String with Cursor Index

Intuition

Store the entire text as a single string and maintain an integer cursor that tracks the cursor position. Every operation manipulates the string around this cursor index. addText inserts at the cursor position, deleteText removes characters before the cursor, and cursorLeft and cursorRight adjust the integer.

This is simple and correct, but inserting into or deleting from the middle of a string costs O(n), where n is the total text length, because every character after the edit point shifts. For this problem's constraints that is acceptable.

Algorithm

  1. Maintain a string text and an integer cursor initialized to 0.
  2. addText(s): Insert s at position cursor in the string. Move cursor forward by s.length.
  3. deleteText(k): Compute toDelete = min(k, cursor). Remove toDelete characters ending at cursor. Move cursor back by toDelete. Return toDelete.
  4. cursorLeft(k): Move cursor left by min(k, cursor). Return the last min(10, cursor) characters to the left of the cursor.
  5. cursorRight(k): Move cursor right by min(k, text.length - cursor). Return the last min(10, cursor) characters to the left of the cursor.

Example Walkthrough

Trace the full example: addText("leetcode"), deleteText(4), addText("practice"), cursorRight(3), cursorLeft(8), deleteText(10), cursorLeft(2), cursorRight(6). Each state shows the string after the operation; the label records the cursor index and the value returned.

1addText("leetcode"): insert at cursor, text="leetcode", cursor=8
0
l
1
e
2
e
3
t
4
c
5
o
6
d
7
e
1/8

The constructor and both addText calls return null, so the full output is [null, null, 4, null, "etpractice", "leet", 4, "", "practi"].

Code

Every insert and delete here shifts characters that are nowhere near the cursor. The next approach splits the text at the cursor so that every modification happens at the end of a structure, where it costs O(1).

Approach 2: Two Stacks (Optimal)

Intuition

The cursor divides the text into two parts: everything to its left and everything to its right. Store the left part in one stack and the right part in another, and every operation becomes an operation on the top of a stack, which is O(1) per character.

The left stack keeps the character closest to the cursor on top. The right stack does the same. The cursor sits between the two stack tops.

  • addText: Push each character onto the left stack. They land right at the cursor.
  • deleteText: Pop from the left stack. This removes the characters immediately to the left of the cursor.
  • cursorLeft: Pop from the left stack and push onto the right stack. The character that was immediately left of the cursor is now immediately right of it.
  • cursorRight: Pop from the right stack and push onto the left stack. The character that was immediately right of the cursor is now immediately left of it.

Every operation touches only the tops of the stacks, so there is no shifting, slicing, or reallocation of distant characters.

Algorithm

  1. Initialize two stacks: left and right. left holds characters to the left of the cursor (top = closest to cursor). right holds characters to the right (top = closest to cursor).
  2. addText(s): Push each character of s onto left.
  3. deleteText(k): Pop min(k, left.size) characters from left. Return the count popped.
  4. cursorLeft(k): Pop min(k, left.size) characters from left, pushing each onto right. Then return the last min(10, left.size) characters from left (peek from the top down).
  5. cursorRight(k): Pop min(k, right.size) characters from right, pushing each onto left. Then return the last min(10, left.size) characters from left.

Example Walkthrough

1TextEditor(): left stack empty
1/9
1TextEditor(): right stack empty
1/9

Code

A doubly linked list with a pointer to the cursor node achieves the same O(k) bounds and is the other common implementation. The two stacks produce the same behavior with less pointer bookkeeping and contiguous memory, since each stack is an array.