算法(十一)字符串

leetcode

14. 最长公共前缀

题目

编写一个函数来查找字符串数组中的最长公共前缀。如果不存在公共前缀,返回空字符串 “”。

示例 1:

输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:

输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

题解

遍历数据过程中记录最短公共子串,示例代码如下所示:

class Solution {
public:
    string shortest_common_prefix(string& str, string& prefix) {
        int length = min(str.size(), prefix.size());
        int idx = 0;
        for (; idx < length; ++idx) {
            if (str[idx] != prefix[idx]) {
                break;
            }
        }
        return str.substr(0, idx);
    }

    string longestCommonPrefix(vector<string>& strs) {
        string prefix = strs[0];
        for (int i = 1; i < strs.size(); ++i) {
            prefix = shortest_common_prefix(strs[i], prefix);
            if (!prefix.size()) {
                break;
            }
        }

        return prefix;
    }
};

复杂度

时间复杂度: O ( m n ) O(mn) O(mn),m为数组中字符串的平均长度,n为数组长度
空间复杂度: O ( 1 ) O(1) O(1)

[hot] 22. 括号生成

题目

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
示例 2:

输入:n = 1
输出:["()"]

题解

递归解决问题,此题比较抽象,递归函数中left代表的是当前还未填充到目标字符串的左括号个数,right代表的是当前还未填充到目标字符串的右括号个数,下面两种情况下需要往字符串中添加左括号:

  1. left > 0 且 left < right -> 左括号还未与右括号匹配上
  2. left == right -> 左括号已经与右括号匹配完成,但是没有完成目标字符串构建

向字符串中添加右括号的唯一情况是所有的左括号都已经填充完毕,示例代码如下:

class Solution {
public:
    vector<string> generateParenthesis(int n) {
        dfs("", n, n);
        return res;
    }

    void dfs(string s, int left, int right) {
        if (left == 0 && right == 0) {
            res.emplace_back(s);
            return;
        }

        if (left == right) {
            dfs(s + "(", left - 1, right);
            return;
        } else if (left < right) {
            if (left > 0) {
                dfs(s + "(", left - 1, right);
            }
            dfs(s + ")", left, right - 1);
        }
    }
private:
    vector<string> res;
};

复杂度

时间复杂度: O ( 4 n n ) O(\frac{4^n}{\sqrt n}) O(n 4n),卡特兰数n的取值个数为 C 2 n n − C 2 n n + 1 C_{2n}^n-C_{2n}^{n+1} C2nnC2nn+1,渐进区间为 O ( 4 n n n ) O(\frac{4^n}{n\sqrt n}) O(nn 4n),每个符合要求的字符串拷贝需要 O ( n ) O(n) O(n)的时间,因而最终的时间复杂度为 O ( 4 n n ) O(\frac{4^n}{\sqrt n}) O(n 4n),卡特兰数教程见 这里
在这里插入图片描述

空间复杂度: O ( 2 n ) O(2n) O(2n),最多递归2n次

[hot] 32. 最长有效括号

题目

给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:

输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:

输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3:

输入:s = ""
输出:0

题解

始终保持栈底元素为当前已经遍历过的元素中「最后一个没有被匹配的右括号的下标」,栈里其他元素维护左括号的下标,具体解释可以参照这里,示例代码如下所示:

class Solution {
public:
    int longestValidParentheses(string s) {
        int length = s.size();
        if (length == 0) {
            return 0;
        }

        int result = 0;
        stack<int> stk;
        stk.push(-1); // 完美匹配的兜底计算,例如(())
        for (int i = 0; i < length; ++i) {
            if (s[i] == '(') {
                stk.push(i);
            } else {
                stk.pop();
                
                // 防止开始就是),导致错误计算
                if (stk.empty()) {
                    stk.push(i); // i是栈底右括号,充当-1兜底的角色
                } else {
                	// 逻辑能走到这里的唯一可能是stk不为空且栈中有(
                	// 这时找到最近的(所组成的pair即为当前最长的有效括号
                    result = max(result, i - stk.top());
                }
            }
        }

        return result;
    }
};

复杂度

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)

题解2

两遍遍历解决问题,思路见 这里,不需要额外空间,示例代码如下:

class Solution {
public:
    int longestValidParentheses(string s) {
        int res = 0;
        int left = 0, right = 0;
        for (int i = 0; i < s.size(); ++i) {
            if (s[i] == '(') {
                ++left;
            } else {
                ++right;
            }
            if (left == right) {
                res = max(res, 2 * right);
            } else if (right > left) {
                left = 0;
                right = 0;
            }
        }
        left = 0;
        right = 0;
        for (int i = s.size() - 1; i >= 0; --i) {
            if (s[i] == '(') {
                ++left;
            } else {
                ++right;
            }
            if (left == right) {
                res = max(res, 2 * left);
            } else if (left > right) {
                left = 0;
                right = 0;
            }
        }
        return res;
    }
};

复杂度

时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)

[hot] 394. 字符串解码

题目

给定一个经过编码的字符串,返回它解码后的字符串。编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。

你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。

此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

示例 1:
输入:s = "3[a]2[bc]"
输出:"aaabcbc"

示例 2:
输入:s = "3[a2[c]]"
输出:"accaccacc"

题解

深度优先遍历解决问题,示例代码如下所示:

class Solution {
public:
    int get_digit() {
        int res = 0;
        while (ptr < src.size() && isdigit(src[ptr])) {
            res = res * 10 + src[ptr++] - '0';
        }

        return res;
    }

    string get_string() {
        if (ptr >= src.size() || src[ptr] == ']') {
            return "";
        }
        string res;
        if (isdigit(src[ptr])) {
            int r_t = get_digit();
            ++ptr;
            string c = get_string();
            ++ptr;
            while (r_t) {
                --r_t;
                res += c;
            }
        } else if (isalpha(src[ptr])) {
            res = string(1, src[ptr++]);
        }

        return res + get_string();
    }

    string decodeString(string s) {
        src = s;
        return get_string();
    }
private:
    int ptr = 0;
    string src;
};

复杂度

时间复杂度: O ( S + ∣ s ∣ ) O(S+|s|) O(S+s),S表示展开后的字符串长度,|s|代表字符串s的长度。
空间复杂度: O ( ∣ s ∣ ) O(|s|) O(s),递归空间

[hot] 647. 回文字符串

题目

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。

回文字符串 是正着读和倒过来读一样的字符串。

子字符串 是字符串中的由连续字符组成的一个序列。

具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。

示例 1:
输入:s = "abc"
输出:3
解释:三个回文子串: "a", "b", "c"

示例 2:
输入:s = "aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"

题解

直接上代码,示例代码如下所示:

class Solution {
public:
    int countSubstrings(string s) {
        int res = 0;
        for (int i = 0; i < s.size() * 2 - 1; ++i) {
            int l = i / 2;
            int r = i / 2 + i % 2;
            while (l >= 0 && r < s.size() && l <= r && s[l] == s[r]) {
                --l;
                ++r;
                ++res;
            }
        }

        return res;
    }
};

复杂度

时间复杂度: O ( n 2 ) O(n^2) O(n2)
空间复杂度: O ( 1 ) O(1) O(1)

剑指offer

58 I. 左旋转字符串

题目

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

示例 1:

输入: s = "abcdefg", k = 2
输出: "cdefgab"
示例 2:

输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"

题解

先从头到尾旋转,再从s.size() - num两侧分别旋转,示例代码如下

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        if (s.empty() || n <= 0) {
            return s;
        }

		n %= s.size();
        std::reverse(s.begin(), s.end());
        std::reverse(s.begin(), s.begin() + s.size() - n);
        std::reverse(s.begin() + s.size() - n, s.end());

        return s;
    }
};

58 II. 翻转字符串里的单词

题目

给定一个字符串,逐个翻转字符串中的每个单词。

说明:

无空格字符构成一个 单词 。
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

题解

先所有字符翻转,然后在单词内翻转(leetcode上要考虑各种情况,其实没必要),示例代码如下:

class Solution {
public:
    string reverseWords(string s) {
        if (s.empty()) {
            return s;
        }

        reverse(s.begin(), s.end());
        int low = 0;
        int high = 0;
        while (low <= s.size() - 1) {
            while (high < s.size() && s[high] != ' ') {
                ++high;
            }
            reverse(s.begin() + low, s.begin() + high);
            low = high + 1;
            high = low;
        }
    
        return s;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值