【每日一LeetCode】2696. 删除子串后的字符串最小长度 + 2645. 构造有效字符串的最少插入数 + 2707. 字符串中的额外字符

94 阅读4分钟

2696. 删除子串后的字符串最小长度

class Solution:
    def minLength(self, s: str) -> int:
        res = []
        for c in s:
            if c == 'B':
                if res and res[-1] == 'A':
                    res.pop()
                else:
                    res.append(c)
            elif c == 'D':
                if res and res[-1] == 'C':
                    res.pop()
                else:
                    res.append(c)    
            else:
                res.append(c)

        return len(res)      

image.png

class Solution:
    def minLength(self, s: str) -> int:
        stack = []
        for c in s:
            stack.append(c)
            if len(stack) >= 2 and ((stack[-2] == 'A' and stack[-1] == 'B') or (stack[-2] == 'C' and stack[-1] == 'D')):
                stack.pop()
                stack.pop()

        return len(stack)

方法: 栈 O(n)O(n)

class Solution:
    def minLength(self, s: str) -> int:
        stack = []
        for c in s:
            if stack and ((stack[-1] == 'A' and c == 'B') or (stack[-1] == 'C' and c == 'D')):
                stack.pop()
            else:
                stack.append(c)

        return len(stack)

C++

class Solution {
public:
    int minLength(string s) {
        stack<char> st;
        for (char c : s){
            if (!st.empty() && ((st.top() == 'A' && c == 'B') || st.top() == 'C' && c == 'D')){
                st.pop();
            }
            else{
                st.push(c);
            }
        }
        return st.size();        
    }
};

2645. 构造有效字符串的最少插入数

Python3

方法一: 动态规划

动态规划 O(n)O(n)\lgroup O(n)、O(n)\rgroup


class Solution:
    def addMinimum(self, word: str) -> int:
        n = len(word)
        d = [0] * (n+1)  # d[i]: 将 前 i 个 字符 拼凑成 若干个 abc 所需要的 最少 插入数
        for i in range(1, n+1):
            d[i] = d[i-1] + 2
            if i > 1 and word[i-1] > word[i-2]:
                d[i] = d[i-1] - 1
        return d[n]

动态规划 O(n)O(1)\lgroup O(n)、O(1)\rgroup

class Solution:
    def addMinimum(self, word: str) -> int:
        n = len(word)
        pre = 0  # d[i]: 将 前 i 个 字符 拼凑成 若干个 abc 所需要的 最少 插入数
        for i in range(1, n+1):
            cur = pre + 2
            if i > 1 and word[i-1] > word[i-2]:
                cur = pre - 1
            pre = cur
        return pre

image.png

方法二: 直接拼接 O(n)O(1)\lgroup O(n)、O(1)\rgroup

image.png

class Solution:
    def addMinimum(self, word: str) -> int:
        n = len(word)
        res = ord(word[0]) - ord(word[n-1]) + 2
        for i in range(1, n):
            res += (ord(word[i]) - ord(word[i-1]) + 2) % 3
        return res 

image.png

方法三: 计算组数 O(n)O(1)\lgroup O(n)、O(1)\rgroup

image.png

image.png

class Solution:
    def addMinimum(self, word: str) -> int:
        n = len(word)
        cnt = 0
        for i in range(1, n):
            if word[i] <= word[i-1]:
                cnt += 1
        return 3 * (cnt + 1) - n
class Solution:
    def addMinimum(self, word: str) -> int:
        n = len(word)
        cnt = 1   # 注意 这种初始化 方式  统计 最终需 凑成的 abc 组数
        for i in range(1, n):
            if word[i] <= word[i-1]:
                cnt += 1
        return 3 * cnt - n

image.png

C++

方法一: 动态规划

动态规划 O(n)O(n)\lgroup O(n)、O(n)\rgroup

class Solution {
public:
    int addMinimum(string word) {
        int n = word.size();
        vector<int> d(n+1);
        for (int i = 1; i <= n; ++i){
            d[i] = d[i-1] + 2;
            if (i > 1 && word[i-1] > word[i-2]){ // 注意 这里 是 大于
                d[i] = d[i-1] - 1;
            }
        }
        return d[n];       
    }
};

动态规划 O(n)O(1)\lgroup O(n)、O(1)\rgroup

class Solution {
public:
    int addMinimum(string word) {
        int n = word.size();
        int pre = 0;
        int cur = 0;
        for (int i = 1; i <= n; ++i){
            cur = pre + 2;
            if (i > 1 && word[i-1] > word[i-2]){// 两个 字符 可以在 同一个 abc 中 
                cur = pre - 1;
            }
            pre = cur;
        }
        return pre;        
    }
};

方法二: 直接拼接 O(n)O(1)\lgroup O(n)、O(1)\rgroup

class Solution {
public:
    int addMinimum(string word) {
        int n = word.size();
        int res = word[0] - word[n-1] + 2;
        for (int i = 1; i < n; ++i){
            res += (word[i] - word[i-1] + 2) % 3;
        }
        return res;
    }
};

方法三: 计算组数 O(n)O(1)\lgroup O(n)、O(1)\rgroup

class Solution {
public:
    int addMinimum(string word) {
        int n = word.size();
        int cnt = 1;  // 注意 这里的初始化  
        for (int i = 1; i < n; ++i){
            if (word[i] <= word[i-1]){
                cnt += 1;
            }
        }
        return 3 * cnt - n;        
    }
};

2707. 字符串中的额外字符

Python3

方法一: 动态规划O(L+n3)O(L+n)\lgroup O(L+n^3) 、 O(L+n)\rgroup

class Solution:
    def minExtraChar(self, s: str, dictionary: List[str]) -> int:
        # f[i] : 前 i 个字符的 子问题
        n = len(s)
        f = [0] * (n+1)
        d = set(dictionary) # 这样 可以 以 O(1) 的复杂度 进行查找
        for i in range(n):
            f[i+1] = f[i] + 1  # 不选
            for j in range(i+1): # 选
                if s[j:i+1] in d:
                    f[i+1] = min(f[i+1], f[j])

        return f[n]

image.png

方法二:字典树优化 O(max_lL+n2)O(max_lLC+n)\lgroup O(max\_l * L+n^2) 、 O(max\_l * L * C+n)\rgroup

# 字典树 查找某个子串 是否在 字典里
class Trie:
    def __init__(self):
        self.children = [None for _ in range(26)]
        self.isEnd = False
    
    def insert(self, word):
        node = self
        for ch in word:
            k = ord(ch) - ord('a')
            if node.children[k] == None:
                node.children[k] = Trie()
            node = node.children[k]
        node.isEnd = True 

    def track(self, node, ch):
        k = ord(ch) - ord('a')
        if node == None or node.children[k] == None:
            return None, False
        node = node.children[k]
        return node, node.isEnd 

class Solution:
    def minExtraChar(self, s: str, dictionary: List[str]) -> int:
        # f[i] : 前 i 个字符的 子问题
        n = len(s)
        f = [0] * (n+1)
        trie = Trie()
        for e in dictionary:
            trie.insert(reversed(e))
        for i in range(n):
            f[i+1] = f[i] + 1  # 不选
            node = trie 
            for j in range(i, -1, -1): # 选
                node, ok = trie.track(node, s[j])
                if ok:
                    f[i+1] = min(f[i+1], f[j])

        return f[n]

C++

方法一: 动态规划O(L+n3)O(L+n)\lgroup O(L+n^3) 、 O(L+n)\rgroup

class Solution {
public:
    int minExtraChar(string s, vector<string>& dictionary) {
        unordered_set<string> set(dictionary.begin(), dictionary.end());
        int n = s.size();
        vector<int> f(n+1);
        for (int i = 0; i < n; ++i){
            f[i+1] = f[i] + 1; // 不选
            for (int j = 0; j <= i; ++j){
                if (set.count(s.substr(j, i-j+1))){
                    f[i+1] = min(f[i+1], f[j]);
                }
            }
        }
        return f[n];        
    }
};

方法二:字典树优化 O(max_lL+n2)O(max_lLC+n)\lgroup O(max\_l * L+n^2) 、 O(max\_l * L * C+n)\rgroup

// 字典树  查找子串
class Trie{
private:
    vector<Trie*> children;
    bool isEnd;
public:
    Trie():children(26), isEnd(false){}

    void insert(string word){
        Trie* node = this;
        for (char ch : word){
            ch -= 'a';
            if (node->children[ch] == nullptr){
                node->children[ch] = new Trie();
            }
            node = node -> children[ch];
        }
        node->isEnd = true;
    }

    bool track(Trie*& node, char ch){
        if (node == nullptr || node->children[ch - 'a'] == nullptr){
            node = nullptr;
            return false;
        }
        node = node->children[ch - 'a'];
        return node->isEnd;
    }
};


class Solution {
public:
    int minExtraChar(string s, vector<string>& dictionary) {
        int n = s.size();
        vector<int> f(n+1, 0);
        Trie trie;
        for (auto s : dictionary){
            reverse(s.begin(), s.end());
            trie.insert(s);
        }
        for (int i = 0; i < n; ++i){
            f[i+1] = f[i] + 1; // 不选
            Trie* node = &trie;
            for (int j = i; j >= 0; --j){
                if (trie.track(node, s[j])){
                    f[i+1] = min(f[i+1], f[j]);
                }
            }
        }
        return f[n];        
    }
};