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): inserttextat the cursor. After the call, the cursor sits right after the inserted text.deleteText(k): remove up tokcharacters immediately to the left of the cursor (like pressing backspace). Return how many characters were actually removed.cursorLeft(k): move the cursor left up toktimes. It cannot go past the start of the text. Return the lastmin(10, len)characters to the left of the cursor, wherelenis the number of characters left of the cursor.cursorRight(k): move the cursor right up toktimes. It cannot go past the end of the text. Return the same kind of string: the lastmin(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
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
["TextEditor", "addText", "deleteText", "addText", "cursorRight", "cursorLeft", "deleteText", "cursorLeft", "cursorRight"]
[[], ["leetcode"], [4], ["practice"], [3], [8], [10], [2], [6]][null, null, 4, null, "etpractice", "leet", 4, "", "practi"]["TextEditor", "addText", "cursorLeft", "cursorRight"]
[[], ["abc"], [1], [1]][null, null, "a", "a"]Asked At
1 companies| Company | Difficulty | |
|---|---|---|
| Rippling | Hard | View all Rippling questions → |
How to Think About It
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.
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).
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.
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.
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 cursordeleteText(k): delete up to k nodes before cursor, return count deletedcursorLeft(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
What Trips People Up in Real Interviews
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.
Solution Code
O(k) per call for addText, delet timeclass 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)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.