文章

[LeetCode] 每日一题 2296. 设计一个文本编辑器

题目链接

https://leetcode.cn/problems/design-a-text-editor

题目描述

请你设计一个带光标的文本编辑器,它可以实现以下功能:

  • 添加:在光标所在处添加文本。

  • 删除:在光标所在处删除文本(模拟键盘的删除键)。

  • 移动:将光标往左或者往右移动。

当删除文本时,只有光标左边的字符会被删除。光标会留在文本内,也就是说任意时候 0 <= cursor.position <= currentText.length 都成立。

请你实现 TextEditor 类:

  • TextEditor() 用空文本初始化对象。

  • void addText(string text) 将 text 添加到光标所在位置。添加完后光标在 text 的右边。

  • int deleteText(int k) 删除光标左边 k 个字符。返回实际删除的字符数目。

  • string cursorLeft(int k) 将光标向左移动 k 次。返回移动后光标左边 min(10, len) 个字符,其中 len 是光标左边的字符数目。

  • string cursorRight(int k) 将光标向右移动 k 次。返回移动后光标左边 min(10, len) 个字符,其中 len 是光标左边的字符数目。

示例输入

示例

输入:
["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 textEditor = new TextEditor(); // 当前 text 为 "|" 。('|' 字符表示光标)
textEditor.addText("leetcode"); // 当前文本为 "leetcode|" 。
textEditor.deleteText(4); // 返回 4
                          // 当前文本为 "leet|" 。
                          // 删除了 4 个字符。
textEditor.addText("practice"); // 当前文本为 "leetpractice|" 。
textEditor.cursorRight(3); // 返回 "etpractice"
                           // 当前文本为 "leetpractice|". 
                           // 光标无法移动到文本以外,所以无法移动。
                           // "etpractice" 是光标左边的 10 个字符。
textEditor.cursorLeft(8); // 返回 "leet"
                          // 当前文本为 "leet|practice" 。
                          // "leet" 是光标左边的 min(10, 4) = 4 个字符。
textEditor.deleteText(10); // 返回 4
                           // 当前文本为 "|practice" 。
                           // 只有 4 个字符被删除了。
textEditor.cursorLeft(2); // 返回 ""
                          // 当前文本为 "|practice" 。
                          // 光标无法移动到文本以外,所以无法移动。
                          // "" 是光标左边的 min(10, 0) = 0 个字符。
textEditor.cursorRight(6); // 返回 "practi"
                           // 当前文本为 "practi|ce" 。
                           // "practi" 是光标左边的 min(10, 6) = 6 个字符。

提示

  • 1 <= text.length, k <= 40

  • text 只含有小写英文字母。

  • 调用 addText ,deleteText ,cursorLeft 和 cursorRight 的 次数不超过 2 * 10^4 次。

进阶:你能设计并实现一个每次调用时间复杂度为 O(k) 的解决方案吗?

解题思路

在本题中,我们需要设计一个文本编辑器,支持添加、删除文本以及移动光标的操作。考虑到光标的移动和文本编辑的需求,我决定使用双向链表来存储文本。每个字符都作为链表中的一个节点,这样不仅可以方便地添加或删除字符,还能高效地移动光标

为了方便操作,我在一开始构建了一个虚拟的头结点,这样就可以统一处理光标的移动和文本的增删,不需要特别判断链表的边界条件。每次添加文本时,我就在当前光标位置插入新的节点,光标移到新增字符的右边;每次删除时,我就从当前光标左边删除指定数量的字符。对于光标的左右移动,我则通过更新当前节点的指针来实现

代码实现

class TextEditor {
    static class Node {
        char val;
        Node next;
        Node pre;

        public Node(char c) {
            this.val = c;
            this.pre = null;
            this.next = null;
        }
    }

    Node cur;

    public TextEditor() {
        cur = new Node(' ');
    }

    public void addText(String text) {
        for (char c : text.toCharArray()) {
            Node temp = new Node(c);
            if (cur.next != null) {
                cur.next.pre = temp;
            }
            temp.next = cur.next;
            temp.pre = cur;
            cur.next = temp;
            cur = cur.next;
        }
    }

    public int deleteText(int k) {
        int count = 0;
        while (k-- > 0 && cur.pre != null) {
            count++;
            cur = cur.pre;
            if (cur.next != null) {
                cur.next = cur.next.next;
            }
            if (cur.next != null) {
                cur.next.pre = cur;
            }
        }
        return count;
    }

    public String cursorLeft(int k) {
        while (k-- > 0 && cur.pre != null) {
            cur = cur.pre;
        }
        return getLeftTen();
    }

    public String cursorRight(int k) {
        while (k-- > 0 && cur.next != null) {
            cur = cur.next;
        }
        return getLeftTen();
    }

    private String getLeftTen() {
        StringBuilder sb = new StringBuilder();
        Node temp = cur;
        int count = 10;
        while (count-- > 0 && temp.pre != null) {
            sb.append(temp.val);
            temp = temp.pre;
        }
        return sb.reverse().toString();
    }
}

复杂度分析

  • 时间复杂度

    • addText 方法的时间复杂度是 O(m),其中 m 是添加文本的长度。我们需要遍历整个字符串并依次插入节点

    • deleteText 方法的时间复杂度是 O(k),其中 k 是要删除的字符数。每次删除操作仅需要调整几个指针,时间复杂度是常数级别的

    • cursorLeftcursorRight 方法的时间复杂度都是 O(k),其中 k 是光标需要移动的步数。每次移动光标都只需要调整当前节点的指针,复杂度为 O(k)

    • getLeftTen 方法的时间复杂度是 O(1),因为最多需要从当前光标向左遍历 10 个字符

总结

本题设计了一个支持光标操作的文本编辑器。通过使用双向链表来存储文本,不仅能方便地在光标所在位置添加和删除字符,还能高效地处理光标的左右移动操作。在实现中,使用虚拟头结点来统一处理光标的位置,避免了边界条件的特殊处理。这个设计思路简单且高效,适合用于需要频繁修改和查询文本的场景

希望这篇分享能为你带来启发!如果你有任何问题或建议,欢迎在评论区留言,与我共同交流探讨。

License:  CC BY 4.0