leetcode 探索算法面试题汇总——字符串

总结字符串相关的题目,积累:

一、验证回文串

1.1 题目描述

给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。

说明:本题中,我们将空字符串定义为有效的回文串。

示例 1:
输入: “A man, a plan, a canal: Panama”
输出: true

示例 2:
输入: “race a car”
输出: false

1.2 解题思路

对于只包含小写字母的回文串来说,它是是对称串,从前往后遍历的结果和从后往前遍历的结果是一样的,我们可以从字符串的首、尾各取一个字符,判断这两个字符是否相等,如果不一样,说明这个字符串不是回文串,如果这两个字符相等,再取首字符的后一个字符和尾字符的前一个字符,判断它们是否相等,操作和上述相同……,依次比较,直到比较完字符串左右两边的所有字符(前一个字符的下标大于后一个字符的下标),整个操作过程中如果没有不相等的字符,就说明该串是回文串。

在本题中,我们要考虑的稍微比上面的过程复杂一点:1、跳过非数字和非字母的其他字符,取到的字符如果不合法,就继续取下一个,首字符往后取,尾字符往前取;2、比较时忽略字符的大小写,可以将字母用toupper()方法转为大写再比较

1.3 解题代码

// C++
class Solution {
public:
    bool isPalindrome(string s) {
        int len = s.size();
        for (int i=0, j=len-1; i

二、分割回文串

2.1 题目描述

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。

示例:
输入: “aab”
输出:
[
[“aa”,“b”],
[“a”,“a”,“b”]
]

2.2 解题思路

DSF+回溯
首先判断a是不是回文字符,我们发现是,所以将a加入到list中,接着我们看后面的a,依次下去,当我们第一次遍历完整个字符串的时候,result中就加入了[‘a’,‘a’,’'b]。接着我们再次遍历,我们发现aa是回文串,我们将aa加入到list中,然后我们判断b是一个回文字符,我们将b加入到list中,现在result就包含了[‘aa’,‘b’]。接着我们进行第三次遍历,我们发现aab不是一个回文串,并且输入字符的大小就是3,这个时候我们输出result。

2.3 解题代码

class Solution {
    vector> res;
    
    void DFS(string& s, int begin, vector& t){
        if(begin>=s.size())
            res.push_back(t);
        else{
            t.push_back("");
            for(int end=begin; end> partition(string s) {
        vector t;
        DFS(s, 0, t);
        return res;
    }
    

};

三、单词拆分

3.1 题目描述

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。

说明:
拆分时可以重复使用字典中的单词。
你可以假设字典中没有重复的单词。

示例 1:
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以被拆分成 “leet code”。

示例 2:
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
解释: 返回 true 因为 “applepenapple” 可以被拆分成 “apple pen apple”。
注意你可以重复使用字典中的单词。

示例 3:
输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false

3.2 解题思路

这道题是属于动态规划的题目,解决思路是填表法。我们用一个bool类型的数组,数组的值表示当前位置之前的单词能不能拆分,在计算它后面的单词能不能拆分时,用到前面的结果,如果前面的单词不能拆分,就不考虑,如果前面的单词可以拆分,就考虑给它再加上一些字母还能不能拆分。把每一次计算的结果存在数组中,最后只要返回数组中最后一个位置的结果,表示这个单词能不能拆分。
所以递推公式是:leetcode 探索算法面试题汇总——字符串_第1张图片
初始条件是:res[0] = true,表示第0个元素之前是可以被表示的。

3.3 解题代码

class Solution {
public:
    bool wordBreak(string s, vector& wordDict) {
        unordered_set set(wordDict.begin(), wordDict.end());
        vector dp(s.size() + 1, false);
        dp[0] = true;
        for (int i = 0; i <= s.size(); ++i) {
            for (int j = 0; j < i; ++j) {
                if (dp[j] && set.count(s.substr(j, i - j))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp.back();
    }
};

四、有效的字母异位词

4.1 题目描述

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。

示例 1:
输入: s = “anagram”, t = “nagaram”
输出: true

示例 2:
输入: s = “rat”, t = “car”
输出: false

说明:
你可以假设字符串只包含小写字母。

进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?

4.2 解题思路

字符异位词是说用组成单词的全部字母重组合成新的单词,两个字母异位词的单词元素相同,并且每个字母出现的次数也相同,根据这样的思路,先计算两个单词的长度,如果长度不一致返回false,如果长度一致:先遍历第一个单词,用一个数组记录每个字母出现的次数,接着遍历第二个单词,在上个数组中减去第二个单词中每个字母出现的次数,如果哪个字母的次数小于零了,就说明这个字母在第二个单词中出现的次数比在第一个单词中出现的度,返回false,遍历完第二个单词若还没有出现上面返回false的情况就可以返回true了。

4.3 解题代码

class Solution {
public:
    bool isAnagram(string s, string t) {
        if (s.size() != t.size())
            return false;
        
        vector  count(26, 0);
        
        for (auto it : s) {
            ++count[it - 'a'];
        }
        
        //优化方案,少一个for循环
  
        for (auto it : t)
            if (--count[it - 'a'] < 0)
                return false;
                
        return true;        
    }
};

五、字符串中的第一个唯一字符

5.1 题目描述

给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。

示例:
s = “leetcode”
返回 0.

s = “loveleetcode”,
返回 2.

注意事项:您可以假定该字符串只包含小写字母。

5.2 解题思路

遍历这个字符串,用一个数组记录每个字母出现的次数;再次遍历字符串,查看字母出现的次数,当字母出现的次数为1时,返回此时的下标;如果字符串遍历完后,没有找到出现次数为1的字母就返回false。

5.3 解题代码

class Solution {
public:
    int firstUniqChar(string s) {
        
        int abcCount[26] = {0};

        
        for ( auto it : s) {
            ++abcCount[it - 'a'];
        }
        
        for (int i=0; i

六、反转字符串

6.1 题目描述

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

示例 1:
输入:[“h”,“e”,“l”,“l”,“o”]
输出:[“o”,“l”,“l”,“e”,“h”]

示例 2:
输入:[“H”,“a”,“n”,“n”,“a”,“h”]
输出:[“h”,“a”,“n”,“n”,“a”,“H”]

6.2 解题思路

将左右两部分的互换。

6.3 解题代码

class Solution {
public:
    void reverseString(vector& s) {
        int m=0;
        int j=s.size()-1;
         for (int i =0; i 

七、实现Trie(前缀树)

7.1 题目描述

实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。

示例:
Trie trie = new Trie();

trie.insert(“apple”);
trie.search(“apple”); // 返回 true
trie.search(“app”); // 返回 false
trie.startsWith(“app”); // 返回 true
trie.insert(“app”);
trie.search(“app”); // 返回 true

说明:
1、你可以假设所有的输入都是由小写字母 a-z 构成的。
2、保证所有输入均为非空字符串。

7.2 解题思路

构建树节点的结构时,用一个标记表示该节点是否为一个单词,因为题目中说所有字符都是小写字母,所以只用26个子节点就好。

插入思路:从根节点开始,每次判断接下来的这个节点是否存在,如果不存在则创建一个

检索思路:从根节点开始,一个一个字母进行比较,如果发现不同的字符,则表示该字符串在集合中不存在;如果所有的字符全部比较完并且全部相同,还需判断最后一个节点的标志位。

验证公共前缀的思路个检索的差不多,只是不要判断最后的标志位。

7.3 解题代码

// 前缀树的每个节点的构造,其中isWord表示是否有以这个节点结尾的单词
class TrieNode {
public:
    // 因为题目中说所有字符都是小写字母,所以只用26个子节点就好
    TrieNode *child[26];
    bool isWord;
    TrieNode() : isWord(false){
        for (auto &a: child)
            a = nullptr;
    }
};

class Trie {
private:
    TrieNode *root;
public:
    /** Initialize your data structure here. */
    Trie() {
        root = new TrieNode();
    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode *nptr = root;
        for (int i=0; i child[word[i] - 'a'] == NULL)
                nptr -> child[word[i] - 'a']= new TrieNode();
            nptr = nptr -> child[word[i] - 'a'];
        }
                        
        nptr -> isWord = true;
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        if (word.size() == 0)   
                return false;
        TrieNode *nptr = root;
        for (int i=0; ichild[word[i] - 'a'] == NULL)
                return false;
            nptr = nptr -> child[word[i] - 'a'];
        }
        return nptr -> isWord;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        if (prefix.size() == 0)
            return false;
        TrieNode *nptr = root;
        for (int i=0; i child[prefix[i] - 'a'] == NULL)
                return false;
            nptr = nptr -> child[prefix[i] - 'a'];
        }
        return true;
    }
};

你可能感兴趣的:(算法)