关于字符串的几道算法面试题

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

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

示例 1:

输入: "A man, a plan, a canal: Panama" 输出: true

示例 2:

输入: "race a car" 输出: false

注意:'0' + 'a' - 'A' = 'P'

class Solution
{
  public:
    bool isPalindrome(string s)
    {
        int len = s.size();
        if(len <= 1)
        {
            return true;
        }
        int low = 0, high = len - 1;
        while(low < high)
        {
            if(s[low] >= 'a' && s[low] <= 'z' || s[low] >= 'A' && s[low] <= 'Z' || s[low] >= '0' && s[low] <= '9')
            {
                if (s[high] >= 'a' && s[high] <= 'z' || s[high] >= 'A' && s[high] <= 'Z' || s[high] >= '0' && s[high] <= '9')
                {
                    if((s[low] >= '0' && s[low] <= '9' && (s[high] < '0' || s[high] > '9')) || (s[high] >= '0' && s[high] <= '9' && (s[low] < '0' || s[low] > '9')))
                    {
                        return false;
                    }
                    else if (s[low] != s[high] && s[low] + 'a' - 'A' != s[high] && s[low] - 'a' + 'A' != s[high])
                    {
                        return false;
                    }
                    else
                    {
                        low++;
                        high--;
                    }
                }
                else
                {
                    high--;
                }
                
            }
            else
            {
                low++;
            }
        }
        return true;
    }
};

分割回文串

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。

返回 s 所有可能的分割方案。

示例:

输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ]

 

DFS搜索

class Solution
{
    vector<vector<string>> ans;

  public:
    vector<vector<string>> partition(string s)
    {
        vector<string> arr;
        GetAns(s, 0, arr);
        return ans;
    }

    void GetAns(string s, int pos, vector<string> &arr)
    {
        if(pos >= s.size())
        {
            ans.push_back(arr);
            return;
        }
        for (int i = pos; i < s.size(); i++)
        {
            string temp(s.begin() + pos, s.begin() + i + 1);
            if(Check(temp))
            {
                arr.push_back(temp);
                GetAns(s, i + 1, arr);
                arr.pop_back();
            }
        }
    }

    bool Check(string s)
    {
        if(s.size() <= 1)
            return true;
        for (int i = 0, j = s.size() - 1; i < j; i++, j--)
        {
            if(s[i] != s[j])
                return false;
        }
        return true;
    }
};

单词拆分

给定一个非空字符串 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

DP

dp:
class Solution
{
  public:
    bool wordBreak(string s, vector<string> &wordDict)
    {
        if(wordDict.size() <= 0)
        {
            return false;
        }
        if(s.size() <= 0)
        {
            return true;
        }
        map<string, bool> Check;
        for (int i = 0; i < wordDict.size(); i++)
        {
            Check[wordDict[i]] = true;
        }
        vector<bool> dp(s.size(), false);
        for (int i = 0; i < s.size(); i++)
        {
            string temp(s.begin(), s.begin() + i + 1);
            if(Check[temp])
            {
                dp[i] = true;
                continue;
            }
            for (int j = 0; j < i; j++)
            {
                if(dp[j] == true)
                {
                    string temp2(s.begin() + j + 1, s.begin() + i + 1);
                    if(Check[temp2])
                    {
                        dp[i] = true;
                        break;
                    }
                }
            }
        }
        return dp[s.size() - 1];
    }
};

单词拆分II

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,在字符串中增加空格来构建一个句子,使得句子中所有的单词都在词典中。返回所有这些可能的句子。

说明:

  • 分隔时可以重复使用字典中的单词。
  • 你可以假设字典中没有重复的单词。

示例 1:

输入: s = "catsanddog" wordDict = ["cat", "cats", "and", "sand", "dog"] 输出: [   "cats and dog",   "cat sand dog" ]

示例 2:

输入: s = "pineapplepenapple" wordDict = ["apple", "pen", "applepen", "pine", "pineapple"] 输出: [   "pine apple pen apple",   "pineapple pen apple",   "pine applepen apple" ] 解释: 注意你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog" wordDict = ["cats", "dog", "sand", "and", "cat"] 输出: []

 

DP加DFS

class Solution
{
    map<int, vector<string>> save;
    vector<string> ans;

  public:
    vector<string> wordBreak(string s, vector<string> &wordDict)
    {
        if (wordDict.size() <= 0)
        {
            return ans;
        }
        if (s.size() <= 0)
        {
            return ans;
        }
        map<string, bool> Check;
        for (int i = 0; i < wordDict.size(); i++)
        {
            Check[wordDict[i]] = true;
        }
        vector<bool> dp(s.size(), false);
        for (int i = 0; i < s.size(); i++)
        {
            string temp(s.begin(), s.begin() + i + 1);
            if (Check[temp])
            {
                dp[i] = true;
                save[i].push_back(temp);
            }
            for (int j = 0; j < i; j++)
            {
                if (dp[j] == true)
                {
                    string temp2(s.begin() + j + 1, s.begin() + i + 1);
                    if (Check[temp2])
                    {
                        dp[i] = true;
                        save[i].push_back(temp2);
                    }
                }
            }
        }
        if(!dp[s.size() - 1])
            return ans;
        DFS(s.size(), "");
        return ans;
    }

    void DFS(int len, string s)
    {
        if(len == 0)
        {
            ans.push_back(s);
            return;
        }
        for (int i = 0; i < save[len - 1].size(); i++)
        {
            DFS(len - save[len - 1][i].size(), save[len - 1][i]+ (s == ""? "" : " ") + s);
        }
    }
};

实现Trie字典树

实现一个 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

说明:

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

class Trie
{
    struct TrieNode
    {
        bool isWord;
        vector<TrieNode *> children;
        TrieNode()
        {
            isWord = false;
            children = vector<TrieNode *>(26, NULL);
        }
        ~TrieNode()
        {
            for (int i = 0; i < 26; i++)
            {
                if (children[i] != NULL)
                {
                    delete children[i];
                }
            }
        }
    };

    private:
      TrieNode *root;

    public:
      Trie()
      {
          root = new TrieNode();
      }
      ~Trie()
      {
          delete root;
      }

      bool find(string word, TrieNode *&newRoot)
      {
          for (int i = 0; i < word.size(); i++)
          {
              if (newRoot->children[word[i] - 'a'] == NULL)
              {
                  return false;
              }
              newRoot = newRoot->children[word[i] - 'a'];
          }
          return true;
    }

    void insert(string word)
    {
        TrieNode* newRoot = root;
        for (int i = 0; i < word.size(); i++)
        {
            if (newRoot->children[word[i] - 'a'] == NULL)
            {
                newRoot->children[word[i] - 'a'] = new TrieNode();
            }
            newRoot = newRoot->children[word[i] - 'a'];
        }
        newRoot->isWord = true;
    }


    bool search(string word)
    {
        TrieNode *newRoot = root;
        return (find(word, newRoot) && newRoot->isWord);
    }

    bool startsWith(string prefix)
    {
        TrieNode *newRoot = root;
        return find(prefix, newRoot);
    }
};

有效的字母异位词

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

示例 1:

输入: s = "anagram", t = "nagaram" 输出: true

示例 2:

输入: s = "rat", t = "car" 输出: false

说明:

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

class Solution
{
  public:
    bool isAnagram(string s, string t)
    {
        map<char, int> str1;
        map<char, int> str2;
        if(s.size() != t.size())
            return false;
        for(char c : s)
        {
            str1[c]++;
        }
        for(char c : t)
        {
            str2[c]++;
        }
        for(char c : s)
        {
            if(str1[c] != str2[c])
                return false;
        }
        return true;
    }
};

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

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

案例:

s = "leetcode" 返回 0. s = "loveleetcode", 返回 2.

 

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

class Solution
{
  public:
    int firstUniqChar(string s)
    {
        map<char, int> save;
        for(char c : s)
        {
            save[c]++;
        }
        for (int i = 0; i < s.size(); i++)
        {
            if(save[s[i]] == 1)
                return i;
        }
        return -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"]

class Solution
{
  public:
    void reverseString(vector<char> &s)
    {
        int i = 0, j = s.size() - 1;
        for (; i < j; i++, j--)
        {
            swap(s[i], s[j]);
        }
    }
};

单词搜索II

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母在一个单词中不允许被重复使用。

示例:

输入: words = ["oath","pea","eat","rain"] and board = [ ['o','a','a','n'], ['e','t','a','e'], ['i','h','k','r'], ['i','f','l','v'] ] 输出: ["eat","oath"]

说明:

你可以假设所有输入都由小写字母 a-z 组成。

提示:

  • 你需要优化回溯算法以通过更大数据量的测试。你能否早点停止回溯?
  • 如果当前单词不存在于所有单词的前缀中,则可以立即停止回溯。什么样的数据结构可以有效地执行这样的操作?散列表是否可行?为什么? 前缀树如何?如果你想学习如何实现一个基本的前缀树,请先查看这个问题: 实现Trie(前缀树)

 

前缀数加DFS

class Solution
{
  public:
    struct TrieNode
    {
        vector<TrieNode *> children;
        bool isWord;
        TrieNode()
        {
            children = vector<TrieNode *>(26, NULL);
            isWord = false;
        }
        ~TrieNode()
        {
            for (int i = 0; i < 26; i++)
            {
                if (children[i] != NULL)
                {
                    delete children[i];
                }
            }
        }
    };

    class Trie
    {
      public:
        TrieNode *root;
        Trie()
        {
            root = new TrieNode();
        }
        ~Trie()
        {
            delete root;
        }
        void insert(string word)
        {
            TrieNode *newRoot = root;
            for (int i = 0; i < word.size(); i++)
            {
                if (newRoot->children[word[i] - 'a'] == NULL)
                {
                    newRoot->children[word[i] - 'a'] = new TrieNode();
                }
                newRoot = newRoot->children[word[i] - 'a'];
            }
            newRoot->isWord = true;
        }
    };
    void DFS(vector<vector<char>> &board, vector<vector<bool>> &visit, TrieNode *newRoot, int row, int col, string str)
    {
        if (newRoot == NULL)
            return;
        if (newRoot->isWord)
        {
//标记为false,防止出现重复的答案
            newRoot->isWord = false;
            ans.push_back(str);
            //注意如果这里return的话,某些案例下会少答案,原因可想而知
        }
        for (int i = 0; i < 4; i++)
        {
            int newRow = row + dx[i];
            int newCol = col + dy[i];
            if (newRow < 0 || newRow >= borderRow || newCol < 0 || newCol >= borderCol)
                continue;
            if (visit[newRow][newCol])
                continue;
            if (newRoot->children[board[newRow][newCol] - 'a'] == NULL)
                continue;
            visit[newRow][newCol] = true;
            DFS(board, visit, newRoot->children[board[newRow][newCol] - 'a'], newRow, newCol, str + board[newRow][newCol]);
            visit[newRow][newCol] = false;
        }
    }
    vector<string> findWords(vector<vector<char>> &board, vector<string> &words)
    {
        t = new Trie();
        borderRow = board.size();
        if (borderRow == 0 || words.size() == 0)
        {
            return ans;
        }
        borderCol = board[0].size();
        vector<vector<bool>> visit(borderRow, vector<bool>(borderCol, false));
        map<string, bool> check;
        string str = "";
        for(string s : words)
        {
            t->insert(s);
        }
        for (int r = 0; r < borderRow; r++)
        {
            for (int c = 0; c < borderCol; c++)
            {
                if (t ->root ->children[board[r][c] - 'a'] != NULL)
                {
                    visit[r][c] = true;
                    TrieNode *newRoot = t->root->children[board[r][c] - 'a'];
                    DFS(board, visit, newRoot, r, c, str + board[r][c]);
                    visit[r][c] = false;
                }
            }
        }
        return ans;
    }
    private:
      Trie *t;
      int dx[4] = {1, -1, 0, 0};
      int dy[4] = {0, 0, 1, -1};
      int borderRow;
      int borderCol;
      vector<string> ans;
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值