力扣高頻|算法面試題彙總(二):字符串

力扣高頻|算法面試題彙總(二):字符串

力扣鏈接
目錄:

  • 1.驗證迴文串
  • 2.分割回文串
  • 3.單詞拆分
  • 4.單詞拆分 II
  • 5.實現 Trie (前綴樹)
  • 6.單詞搜索 II
  • 7.有效的字母異位詞
  • 8.字符串中的第一個唯一字符
  • 9.反轉字符串

1.驗證迴文串

給定一個字符串,驗證它是否是迴文串,只考慮字母和數字字符,可以忽略字母的大小寫。
說明:本題中,我們將空字符串定義爲有效的迴文串。
示例 1:
輸入: “A man, a plan, a canal: Panama”
輸出: true
示例 2:
輸入: “race a car”
輸出: false

思路:使用兩個指針,一個從左邊開始,一個從右邊開始,挨着比較,需要處理的是:1.大寫變小寫。2. 如果不是字母或者數字,則跳過。
C++

class Solution {
public:
    bool isPalindrome(string s) {
        // 轉換成小寫字母
        transform(s.begin(),s.end(), s.begin(), ::tolower);
        int length = s.size();
        int i = 0;
        int j = length -1;
        while(i < j){
            // 判斷是否是數字和英文字符
            if(!isalnum(s[i])){//!(s[i]<='9' && s[i] >= '0' && s[i] <= 'z' && s[i] >= 'a')
                ++i;
                continue;
            }else if(!isalnum(s[j])){
                --j;
                continue;
            }
            // 轉換大小寫
            char a = s[i];
            char b = s[j];
            if(a == b){
                ++i;
                --j;
            }else{
                return false;
            }
        }
        return true;
    }
};

Python

class Solution:
    def isPalindrome(self, s: str) -> bool:
        pl = 0
        pr = len(s) - 1
        while pl < pr:
            if not s[pl].isalnum():
                pl += 1
                continue
            elif not s[pr].isalnum():
                pr -= 1
                continue
            if s[pl].lower() == s[pr].lower():
                pl += 1
                pr -= 1
            else:
                return False
        return True

2.分割回文串

給定一個字符串 s,將 s 分割成一些子串,使每個子串都是迴文串。
返回 s 所有可能的分割方案。
示例:
輸入: “aab”
輸出:
[
[“aa”,“b”],
[“a”,“a”,“b”]
]

參考回溯法
總結一下:

  • 1.把字符串“aab”,按長度n(n <= 字符串的長度)進行分割。
  • 2.n從1開始。
  • 3.start=0,n=1時,按順序對字符串進行分割,首先得到”a“,判斷滿足迴文要求,進行回溯,end=1。
  • 4.下一次開始,start=1,得到”a“,判斷滿足迴文要求,進行回溯,end=2。
  • 5.下一次開始,start=2,得到”b“,判斷滿足迴文要求,進行回溯,end=3。
  • 6.下一次開始,start=3,已經>=字符串的長度(3),得到分割結果,進行保存,結束回溯,return到上次回溯的的地方,即start=2,然後把res中保存的最後一個結果彈出,此時res中爲[“a”, “a”],並依次回溯到start=0,end=1的地方,此時res中的元素已經全部彈出,爲空。
  • 7.進行下一次for循環,end=2,即分割長度爲2,依次進行判斷…

C++

class Solution {
public:
    // 保存所有的分割結果:
    vector<vector<string>> split_results;
    vector<vector<string>> partition(string s) {
        if(s.size() == 0)
            return split_results;
        // 單次分割成迴文串的結果
        vector<string> res;
        // 使用回溯法進行結果查找
        back(s, 0, res); // s: 完整的字符串, 0: 開始的位置, res: 單次分割的結果
        return split_results;
    }
    void back(string s, int start, vector<string> res){
        // 首先判斷回溯停止條件
        if( start >= s.size()){
            // 保存單次分割結果
            split_results.push_back(res);
            return ;
        }
        for(int end = start + 1; end < s.size() + 1; ++end){
            // 截取字符串
            string split_s = s.substr(start, end - start);
            // 迴文串判斷
            if (isPalindrome(split_s)){
                // 添加當前符合要求的字符串
                res.push_back(split_s);
                // 回溯
                back(s, end, res); 
                // 彈出棧頂
                res.pop_back();
            }
        }
    }
    // 是否是迴文串判斷
    bool isPalindrome(string s){
        if(s.size() == 0)
            return false;
        int start = 0;
        int end = s.size() -1;
        while( start < end){
            if(s[start] == s[end]){
                ++start;
                --end;
            }else{
                return false;
            }
        }
        return true;
    }
    
};

Python

class Solution:
    # def partition(self, s: str) -> List[List[str]]:
    def partition(self, s):
        
        # 保存分割下來的結果
        self.split_results = []
        if len(s) == 0:
            return self.split_results
        # 單次分割的結果
        res = []
        # 回溯法進行查找
        self.back(s, 0, res) # s: 需要分割的字符串 0:起點位置  res:單次分割的結果
        return self.split_results
           
    # 回溯法
    def back(self, s, start, res):
        # 回溯的截止條件
        if start >= len(s):
            # 一次回溯結束
            # 對res進行拷貝,防止彈出時,split_results數據變化
            resCopy = res.copy()
            self.split_results.append(resCopy)
            return
        # 以start開始,截取字符串進行判斷
        # end + 1是爲 star > len(s) 創造結束條件
        for end in range(start + 1, len(s) + 1):
            # 截取字符串
            split_s = s[start: end]
            # 迴文判斷
            if s[start: end] == split_s[::-1]: # 逆轉字符串
                # 是迴文串,則繼續對 end 後面的字符串進行判斷
                # 首先保存單詞結果
                res.append(split_s)
                self.back(s, end, res)
                # 回溯完 彈出最後一個元素
                res.pop()

3.單詞拆分

給定一個非空字符串 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

親測使用暴力法會超時,即把字符串分成兩個部分,分別判斷能否被拆分,依次遍歷。
參考思路
總結:
1.本題判斷是的字符串能否有單詞字典中的值組成,不需要判斷如何組成。
2.基本思路是把整個字符串挨個拆分,但是拆分過的字符串就已經判斷過能否被拆分,則不需要再重複判斷,暴力法會重複判斷導致超時。
3.根據第2點,可以創建一個數組isBreak,來保存整個數組能否被拆分,但注意的是isBreak的長度是是字符串s的長度+1。
4.解釋第3點,當判斷字符串索引爲i的位置時(即得到從0開始到位置i的字符串),即只需要判斷在i之前所有能被分割的位置j到i的子字符串能否被分割,C++寫法:s.substr(j, i-j),Python寫法:s[j: i],這兩種寫法都是左閉右開,所以需要默認isBreak[0] = true,以及isBreak的長度必須是字符串s的長度+1。
C++:

class Solution {
public:
   bool wordBreak(string s, vector<string>& wordDict) {
       
       // 暴力拆分會超時
       // 只需要關係能否被分割,不需要關心怎麼被分割
       // 從頭開始遍歷判斷,記錄每個位置是否能被分割。
       // 只需要判斷從i之前所有可以被切分的位置j到i的子串能否在字典中找到即可
       int length = s.size();
       if(length == 0)
           return false;
       // 用一個變量記錄位置i能否被分割
       vector<bool> isBreak(length + 1, false);
       // isBreak表示從0開始,相隔n的字符串能否被分割
       // 這是要和s.substr(j, i - j)含義對應起來, 分割從j開始的i-j的字符串
       isBreak[0] = true;// id = 0 空字符串默認爲true
       for(int i = 1; i <= length; ++i){
           for(int j = 0; j< i; ++j){
               if(isBreak[j] && find(wordDict.begin(), wordDict.end(), s.substr(j, i - j)) !=  wordDict.end())
                   isBreak[i] = true; // 當i之前所有可以被切分的位置j開始,切割子字符串,能找到,則isBreak[i] = true
           }
       }
       // 要判斷整個字符串能不能被分割,返回最後一個判斷即可
       return isBreak[length];
    }
};

Python:

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        length = len(s)
        if length == 0:
            return False
        isBreak = [False] * (length + 1)
        isBreak[0] = True
        for i in range(1, length + 1):
            for j in range(0, i):
                if (isBreak[j] == True) and ( s[j: i] in wordDict):
                    isBreak[i] = True
        
        return isBreak[length]
                

4.單詞拆分 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”]
輸出:
[]

我首先嚐試了一下回溯法,成功超時。。

class Solution {
public:
    vector<string> results;
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        
        int length = s.size();
        vector<string> result;
        // 使用回溯法進行記錄
        // s:完整的字符串 result: 單次處理的結果 0:開始的位置 wordDict:單詞字典
        back(s , result, 0, wordDict);
        return results;
    }
    void back(string s, vector<string> result, int start, vector<string>& wordDict){
        int length = s.size();
        // 邊界條件
        if( start >= length){
            // 對結果進行處理
            string res;
            for(auto itear = result.begin(); itear != result.end(); ++itear)
                if( itear != result.end() - 1)
                    res += *itear + " ";
                else
                    res += *itear;
            // 添加答案
            results.push_back(res);
            return ; //結束
        }
        for(int end = start + 1; end < length + 1; ++end){
            // 判斷能否被分割
            if( find(wordDict.begin(), wordDict.end(), s.substr(start, end - start))!=wordDict.end()){// 能被分割
                result.push_back( s.substr(start, end - start)); // 添加到結果中
                // 回溯
                back(s, result, end, wordDict);
                result.pop_back();
            }
        }
        
    }
};

思路一:
使用帶記憶的回溯法
在之前的方法中,可以看出許多子問題的求解都是冗餘的,造成了大量的重複計算。
爲了避免這種情況,使用一個哈希表[key:valuekey:value]來表示結果。其中keykey是子字符串,valuevalue則是所有分割句子的結果。使用的回溯函數爲:vector<string> back(string s, vector<string>& wordDict),表示字符串s使用單詞字典wordDict分割句子的結果
整體流程:

  • 1.首先在字符串s中查找單詞字典的結果,如果找到了,則切割該字符串,並對剩下的子字符串s.substr(word.size()) , wordDict繼續進行查找。
  • 2.一直查找,直到把字符串切割爲空"",則開始回溯:if(s.empty()) return {""};vector<string> temp = back(s.substr(word.size()) , wordDict);
  • 3.比如輸入s="catsand",字典wordDict={"cat","cats","sand","and"}。第一次查找時,發現"cat"在字符串中,切割字符串之後s="sand",繼續查找,發現"sand" 在字符串中,切割字符串之後s="",繼續查找,達到邊界條件:if(s.empty()) return {""};,回溯:vector<string> temp = back(s.substr(word.size()),此時temp="",通過result.push_back(word+(tmp.empty()?"":" "+tmp));把回溯結果放在result中,此時result=" sand"。依次回溯到查找到"cat"結果中,此時result="cat sand"。根據for(auto word : wordDict)得指完成"cat"爲首的切割之後,查找下個單詞字典"cats"是否在字符串s中,依此類推。
  • 4.其中if(hashWords.count(s)) return hashWords[s];// 如果有則返回結果可以省去重新分割的結果,大量節省時間。

C++

class Solution {
public:
    vector<string> results;
    // 構造哈希表,存儲當字符串爲s時,可以分割的結果
    map<string, vector<string>> hashWords;
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        int length = s.size();
        vector<string> result;
        // 使用回溯法進行記錄
        // s:字符串  wordDict:單詞字典
        // 返回值:s字符串所有分割成句子的結果
        return back(s , wordDict);
    }
    vector<string> back(string s, vector<string>& wordDict){
        // 首先判斷哈希表中有沒有字符串s分割的結果,避免重複計算
        if(hashWords.count(s)) return hashWords[s];// 如果有則返回結果
        // 邊界條件,s爲空
        if(s.empty()) return {""};
        // 使用result來保存字符串s分割的所有結果
        vector<string> result;
        // 按個使用單詞字典對字符串進行判斷
        for(auto word : wordDict){
            // 如果當前字符串s(從左往右找,避免重複查找)找到對應的單詞:
            if(s.substr(0, word.size()) == word){
                // 找到了,則進行回溯繼續查找
                vector<string> temp =  back(s.substr(word.size()) , wordDict);
                for(auto tmp : temp){
                    result.push_back(word+(tmp.empty()?"":" "+tmp));
                }
            }else{
                continue;
            }
        }
        hashWords[s] = result;     
        return hashWords[s];    
    }   
};

Python:

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
        # 哈希表 字典
        self.hashWords = {}
        length = len(s)
        if length == 0:
            return [""]
        # 回溯查找
        return self.back(s, wordDict)
    def back(self, s, wordDict):
        # 首先查找哈希表,判斷有無結果
        if s in self.hashWords:
            return self.hashWords[s]
        if len(s) == 0:
            return [""]
        results = []
        for i in range(len(s) + 1):
            if s[:i] in wordDict:
                temp = self.back(s[i:], wordDict)
                for tmp in temp:
                    results.append(s[:i] +("" if len(tmp)== 0 else " ") +tmp)
        self.hashWords[s] = results
        return self.hashWords[s]

思路二:
動態規劃,暫時不會,等會了再補上。

5.實現 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 構成的。
保證所有輸入均爲非空字符串。

參考這篇的思路:
1.構建類似於鏈表的結構,一個根節點有26個分支(對應26個小寫字母)。
2.插入的時候,在對應位置進行插入,位置索引爲:a-'a',全部插入完之後,給一個標誌表明是單詞:isWord = true
3.尋找的時候,首先判斷該索引位置是否爲NULL,如果不是則繼續搜索,否則返回false,只是需要判斷isWord的值。
4.查詢開頭和尋找單詞基本一致。

C++

// 新建一個數據集結果,表示樹的結點
class TrieNode{
public:
    // 26個小寫字母 所以有26個分支
    TrieNode * children[26];
    bool isWord; // 表示是否是一個單詞
    // 構造函數
    TrieNode() : isWord(false){
        for (auto &child : children) child = NULL;
    }
};
class Trie {
public:
    /** Initialize your data structure here. */
    Trie() {
        // 根結點
        root = new TrieNode();
    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode* p = root;
        for(auto a : word){
            int i = a - 'a';
            // 如果不存在做個前綴,則新建一個
            if(!p->children[i])
                 p->children[i] = new TrieNode();
            // 移動位置
            p = p->children[i];
        }
        // 標記
        p->isWord = true;
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode* p = root;
        // 遍歷word
        for(auto a: word){
            int i = a - 'a';
            // 如果不存在做個前綴
            if(!p->children[i]) return false;
            // 移動位置
            p = p->children[i];
        }
        // 成功遍歷完之後
        return  p->isWord; // 比如單詞有apple ,但搜索app,app不是單詞
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        TrieNode* p = root;
        // 遍歷word
        for(auto a: prefix){
            int i = a - 'a';
            // 如果不存在做個前綴
            if(!p->children[i]) return false;
            // 移動位置
            p = p->children[i];
        }
        // 成功遍歷完之後
        return true;
    }
private:
    TrieNode* root;
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */

Python

class TrieNode:
    def __init__(self):
        self.isWord = False
        self.children = [None]*26
class Trie:
    
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.root = TrieNode()
        

    def insert(self, word: str) -> None:
        """
        Inserts a word into the trie.
        """
        p = self.root
        for a in word:
            # 需要轉換成ascii碼
            index = ord(a) - ord('a')
            if p.children[index] == None:
                # 新建結點
                p.children[index] = TrieNode()
            # 跳轉鏈接
            p = p.children[index]
        # 全部插值完畢 修改flag
        p.isWord = True

    def search(self, word: str) -> bool:
        """
        Returns if the word is in the trie.
        """
        p = self.root
        for a in word:
            index = ord(a) - ord('a')
            if p.children[index] == None:
                return False
            p = p.children[index]
        return p.isWord

    def startsWith(self, prefix: str) -> bool:
        """
        Returns if there is any word in the trie that starts with the given prefix.
        """
        p = self.root
        for a in prefix:
            index = ord(a) - ord('a')
            if p.children[index] == None:
                return False
            p = p.children[index]
        return True
# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)

6.單詞搜索 II

給定一個二維網格 board 和一個字典中的單詞列表 words,找出所有同時在二維網格和字典中出現的單詞。
單詞必須按照字母順序,通過相鄰的單元格內的字母構成,其中“相鄰”單元格是那些水平相鄰或垂直相鄰的單元格。同一個單元格內的字母在一個單詞中不允許被重複使用。
示例:
輸入:
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(前綴樹)。

參考力扣官方實現

使用的是帶前綴樹的回溯
爲了更好地理解回溯過程,在下面的動畫中演示如何在 Trie 中找到 dog,來自力扣官方
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
優化:

  • 沿着 Trie 的節點回溯。
    可以簡單地使用 Trie 作爲字典來快速找到單詞和前綴的匹配,即在回溯的每一步,我們都從Trie 的根開始。
  • 在回溯過程中逐漸剪除 Trie 中的節點(剪枝)。
    在這裏插入圖片描述
    Python:
class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        # 在字典中記錄單詞
        WORD_KEY = '$'
        # 構建前綴樹方便遍歷
        Trie = {}
        # 遍歷單詞字典,建立前綴樹
        for word in words:
            # 遍歷單詞
            node = Trie
            for letter in word:
                # 如果沒有這個前綴,則構建,返回鍵值,作爲下個字典的前綴(鍵)
                node = node.setdefault(letter, {})
            # 遍歷完單詞後,在該層記錄一個單詞標記
            node[WORD_KEY] = word
        # 用來保存所有遍歷查找的結果
        matchWords = []
        # 矩陣的行和列 
        rows, cols = len(board), len(board[0])
        # 準備進行回溯
        def back( node, rowID, colID):
            # 獲取當前座標的值
            letter = board[rowID][colID]
            # 獲取當前結點
            curNode = node[letter]
            # 首先考慮邊界條件
            # 查找當前能不能構成一個單詞
            wordFind = curNode.pop(WORD_KEY, False)
            # 如果查找到了,放進結果中
            if wordFind:
                matchWords.append(wordFind)
            # 標記當前結點
            board[rowID][colID] = '#'
            for rowOffset, colOffset in [[0,1],[1,0],[-1,0],[0,-1]]:
                newRow = rowID + rowOffset
                newCol = colID + colOffset
                # 越界跳過
                if newRow < 0 or newRow >= rows or newCol < 0 or newCol >= cols:
                    continue
                # 已經訪問跳過或者不存在前綴樹的結點中
                if not board[newRow][newCol] in curNode:
                    continue
                # 遍歷
                back(curNode, newRow, newCol)
            # 回溯
            board[rowID][colID] = letter
            # 優化:增量地刪除Trie中匹配的葉子節點
            # 訪問過最後一個結點,遍歷完就可以刪除剪枝
            if not curNode:
                node.pop(letter)

        # 遍歷
        for row in range(rows):
            for col in range(cols):
                # 以board[row][col]爲起點遍歷, 首先要判斷前綴樹中是否有這個jiedian
                if board[row][col] in Trie:
                    # Tire: 前綴樹 board: 矩陣 row、col:座標
                    back(Trie, row, col)
                
        return matchWords

7.有效的字母異位詞

給定兩個字符串 s 和 t ,編寫一個函數來判斷 t 是否是 s 的字母異位詞。
示例 1:
輸入: s = “anagram”, t = “nagaram”
輸出: true
示例 2:
輸入: s = “rat”, t = “car”
輸出: false
說明:
你可以假設字符串只包含小寫字母。
進階:
如果輸入字符串包含 unicode 字符怎麼辦?你能否調整你的解法來應對這種情況?

思路1:
比較暴力的方法,對字符串進行排序,再挨個比較。
C++

class Solution {
public:
    bool isAnagram(string s, string t) {
        int lenS = s.length();
        int lenT = t.length();
        if(lenS != lenT){
            return false;
        }
        sort(s.begin(), s.end());
        sort(t.begin(), t.end());
        for(int i = 0; i < lenS; ++i){
            if(s[i] != t[i])
                return false;
        }
        return true;
    }
};

Python:

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False
        return sorted(s) == sorted(t)

思路2:
使用哈希表,統計字符串s和t中各個字符出現的次數,使用一個哈希表足夠,第一次遍歷s統計各字符次數。第二次遍歷t,減去各字符出現的次數,最後再統計哈希表是否都爲0。
C++

class Solution {
public: 
    bool isAnagram(string s, string t) {
        int lenS = s.length();
        int lenT = t.length();
        if(lenS != lenT){
            return false;
        }
        map<char, int> idCount;
        for(int i = 0; i < lenS; ++i){
            ++idCount[s[i]]; 
            --idCount[t[i]];
        }
        for(auto itear = idCount.begin(); itear != idCount.end(); ++itear){
            if(itear->second != 0)
                return false;
        }
        return true;
    }
};

Python:

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False
        idHash = {}
        for i in range(len(s)):
            # 字符第一次出現
            if not s[i] in idHash:
                idHash[s[i]] = 1
            else :
                idHash[s[i]] += 1
            if not t[i] in idHash:
                idHash[t[i]] = -1
            else :
                idHash[t[i]] -= 1
        for value in idHash.values():
            if value != 0:
                return False
        return True

8.字符串中的第一個唯一字符

給定一個字符串,找到它的第一個不重複的字符,並返回它的索引。如果不存在,則返回 -1。
案例:
s = “leetcode”
返回 0.
s = “loveleetcode”,
返回 2.
注意事項:您可以假定該字符串只包含小寫字母。

思路:
構建哈希表,第一次遍歷,統計每個字符出現的次數,第二次遍歷,找到只出現一次的字符。
C++

class Solution {
public:
    int firstUniqChar(string s) {
        map<char, int> idHash;
        for(int i = 0; i < s.length(); ++i){
            // 字符第一次找到
            if( idHash.find(s[i]) == idHash.end())
                idHash[s[i]] = 1;
            else
                --idHash[s[i]];
        }
        // 第二次遍歷,找到第一個哈希表值爲1的字符
        for(int i = 0; i < s.length(); ++i){
            if(idHash[s[i]] == 1)
                return i;
        }
        return -1;
    }
};

Python:

class Solution:
    def firstUniqChar(self, s: str) -> int:
        idHash = {}
        for i in range(len(s)):
            # 第一次遇到字符
            if not s[i] in idHash:
                idHash[s[i]] = 1
            else:
                idHash[s[i]] -= 1
        for i in range(len(s)):
            if  idHash[s[i]] == 1:
                return i
        return -1

9.反轉字符串

編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 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”]

思路:使用兩個指針,分別指向字符串的開始和結尾,使用中間變量temp完成前後字符交換。
C++

class Solution {
public:
    void reverseString(vector<char>& s) {
        int pStart = 0;
        int pEnd = s.size()-1;
        char temp;
        while(pStart < pEnd){
            temp = s[pStart];
            s[pStart] = s[pEnd];
            s[pEnd] = temp;
            ++pStart;
            --pEnd;
        }
        
    }
};

Python

class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        pStart = 0
        pEnd = len(s) - 1
        while pStart < pEnd:
            s[pStart], s[pEnd] = s[pEnd], s[pStart]
            pStart += 1
            pEnd -= 1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章