HardLinked ListStringStackDesignSimulationDoubly-Linked ListUpdated Sep 2026

2296. Design a Text Editor

Asked at Rippling

Difficulty

Hard

Time

O(k) per call for addText, delet

Space

O(n)

Pattern

Linked List

Companies

1

Problem

Design a text editor class that keeps a cursor inside a piece of text. The cursor is a position between characters, and it always satisfies 0 <= cursor <= text.length.

Implement these operations:

  • addText(text): insert text at the cursor. After the call, the cursor sits right after the inserted text.
  • deleteText(k): remove up to k characters immediately to the left of the cursor (like pressing backspace). Return how many characters were actually removed.
  • cursorLeft(k): move the cursor left up to k times. It cannot go past the start of the text. Return the last min(10, len) characters to the left of the cursor, where len is the number of characters left of the cursor.
  • cursorRight(k): move the cursor right up to k times. It cannot go past the end of the text. Return the same kind of string: the last min(10, len) characters to the left of the cursor.

All characters are lowercase English letters. The tricky part is that k can be larger than the available room, so moves and deletes must stop at the text boundary.

Cursor sits between characters

textleetpractice01234567891011cursorAfter addText("leetcode") then deleteText(4) then addText("practice"), thecursor is after "leet".textdeleteText(10) removes these 4leetpractice01234567891011cursordeleteText only removes characters to the left of the cursor, and stops when itruns out.
The cursor is a gap in the text. Insert happens at the gap, delete removes characters just left of the gap.

Constraints

  • ●1 <= text.length, k <= 40
  • ●text consists of lowercase English letters
  • ●At most 2 * 10^4 calls in total will be made to addText, deleteText, cursorLeft and cursorRight

Examples

Example 1
Input["TextEditor", "addText", "deleteText", "addText", "cursorRight", "cursorLeft", "deleteText", "cursorLeft", "cursorRight"] [[], ["leetcode"], [4], ["practice"], [3], [8], [10], [2], [6]]
Output[null, null, 4, null, "etpractice", "leet", 4, "", "practi"]
WhyStart empty. addText("leetcode") gives "leetcode|". deleteText(4) removes the 4 characters before the cursor, leaving "leet|", so it returns 4. addText("practice") gives "leetpractice|". cursorRight(3) cannot move because the cursor is already at the end, and the last 10 characters to the left are "
Example 2
Input["TextEditor", "addText", "cursorLeft", "cursorRight"] [[], ["abc"], [1], [1]]
Output[null, null, "a", "a"]
WhyAfter addText("abc") the text is "abc|". cursorLeft(1) moves to "ab|c" and returns the 1 character to the left: "a". cursorRight(1) moves back to "abc|" and again returns the last min(10, 3) = 3 characters to the left, which is "abc"... but the cursor is at the end, so the answer is "abc". Wait, the

Asked At

1 companies
CompanyDifficulty
RipplingHardView all Rippling questions →

How to Think About It

1

Brute force: use a string or array to store text. Each insert/delete shifts elements. That's O(n) per operation. The left/right moves are O(1) but text operations are slow.

2

Better approach: use a doubly linked list. Each character is a node. The cursor is a pointer. Insert adds a node after the cursor. Delete removes the node before the cursor. Left/right moves the pointer. All operations are O(1).

3

The doubly linked list approach: maintain a cursor pointer. Insert(x): add node with value x after cursor, move cursor forward. Delete(): remove node before cursor. MoveLeft(k): move cursor back k steps. MoveRight(k): move cursor forward k steps. Print(): collect all nodes and return as string.

4

For print, you need to traverse from the head to the end. The doubly linked list has O(n) print but O(1) for all other operations. Since print is called infrequently, this is acceptable.

5

Edge cases: cursor at beginning (can't move left), cursor at end (can't move right), deleting from empty text, inserting into empty text, moveLeft/moveRight with k larger than available positions.

Optimal Approach

Data structure: doubly linked list with dummy head and tail nodes.

  • cursor: pointer to the current node (starts at dummy head)
  • addText(text): for each character, insert a new node after cursor, advance cursor
  • deleteText(k): delete up to k nodes before cursor, return count deleted
  • cursorLeft(k): move cursor back k steps (min k, available steps)
  • cursorRight(k): move cursor forward k steps (min k, available steps)
  • textEditorText(): traverse from head to tail, collect characters, return string

The doubly linked list gives O(1) insert/delete/move and O(n) for printing.

Walkthrough:
addText("leetcode"): list = [dummy,l,e,e,t,c,o,d,e]. cursor at e.
cursorLeft(4): cursor moves to t.
deleteText(5): remove e,t,c,o,d. list = [dummy,l,e,e]. cursor at e (second).
addText("practice"): list = [dummy,l,e,e,p,r,a,c,t,i,c,e]. cursor at e.
textEditorText(): return "leetcodepractice" (traverse from first real node).

Doubly linked list with dummy ends

after deleteText(10)practicenullDeleting walks left from the cursor, unlinking nodes until k runs out or thedummy head is reached.addText: insert after cursor, cursor = new nodedeleteText: unlink cursor.prev while k > 0cursorLeft/Right: walk prev/next, stop at dummy
A dummy head and tail remove all boundary checks. The cursor is just a node pointer.
ComplexityTime O(k) per call for addText, deletSpace O(n)Each character is stored once in a doubly linked list, and every operation touches at most k nodes.

What Trips People Up in Real Interviews

1

Using a string or array. Insert and delete in the middle of a string are O(n) due to shifting. A doubly linked list gives O(1) for these operations.

2

Forgetting dummy head and tail nodes. Without them, you need null checks for insert at the beginning and delete at the beginning. Dummy nodes eliminate edge cases.

3

Confusing cursor position with array index. The cursor is a pointer to a node, not an integer index. Moving left means following prev pointers, not decrementing an index.

4

Not handling the case where cursorLeft or cursorRight tries to move past the boundaries. Clamp the movement: cursorLeft stops at head, cursorRight stops at tail.prev.

5

Deleting from the wrong side. Delete removes the character BEFORE the cursor (to the left of it), not after it. This matches most text editors' behavior.

Solution Code

O(k) per call for addText, delet time
class Node:
    def __init__(self, val=''):
        self.val = val
        self.prev = None
        self.next = None

class TextEditor:
    def __init__(self):
        self.head = Node()
        self.tail = Node()
        self.head.next = self.tail
        self.tail.prev = self.head
        self.cursor = self.head

    def addText(self, text):
        for ch in text:
            node = Node(ch)
            node.prev = self.cursor
            node.next = self.cursor.next
            self.cursor.next.prev = node
            self.cursor.next = node
            self.cursor = node

    def deleteText(self, k):
        deleted = 0
        while k > 0 and self.cursor != self.head:
            prev = self.cursor.prev
            prev.next = self.cursor.next
            self.cursor.next.prev = prev
            self.cursor = prev
            k -= 1
            deleted += 1
        return deleted

    def cursorLeft(self, k):
        while k > 0 and self.cursor != self.head:
            self.cursor = self.cursor.prev
            k -= 1

    def cursorRight(self, k):
        while k > 0 and self.cursor.next != self.tail:
            self.cursor = self.cursor.next
            k -= 1

    def textEditorText(self):
        result = []
        node = self.head.next
        while node != self.tail:
            result.append(node.val)
            node = node.next
        return ''.join(result)

Pro at DSA?

Test your skills with a real FAANG-style mock interview.

Start a Mock Interview →

Frequently Asked Questions

What is the Design a Text Editor problem?

Design a text editor with cursor movement (left, right), text insertion, text deletion, and a print operation. The cursor starts at position 0. This problem tests your ability to model cursor-based editing with appropriate data structures.

How do you solve Design a Text Editor?

The optimal approach runs in O(k) per call for addText, delet time and O(n) space. Scroll up to "Optimal Approach" for the step-by-step walkthrough, diagrams, and code in Python, C++ and Java.

What companies ask Design a Text Editor?

Design a Text Editor is asked at Rippling. It is a hard difficulty problem.

What are common mistakes on Design a Text Editor?
  • Using a string or array. Insert and delete in the middle of a string are `O(n)` due to shifting. A doubly linked list gives `O(1)` for these operations.
  • Forgetting dummy head and tail nodes. Without them, you need null checks for insert at the beginning and delete at the beginning. Dummy nodes eliminate edge cases.
  • Confusing cursor position with array index. The cursor is a pointer to a node, not an integer index. Moving left means following prev pointers, not decrementing an index.
  • Not handling the case where cursorLeft or cursorRight tries to move past the boundaries. Clamp the movement: cursorLeft stops at head, cursorRight stops at tail.prev.
  • Deleting from the wrong side. Delete removes the character BEFORE the cursor (to the left of it), not after it. This matches most text editors' behavior.