力扣高頻|算法面試題彙總(二):字符串
力扣鏈接
目錄:
- 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();
}
}
}
};
思路一:
使用帶記憶的回溯法。
在之前的方法中,可以看出許多子問題的求解都是冗餘的,造成了大量的重複計算。
爲了避免這種情況,使用一個哈希表[]來表示結果。其中是子字符串,則是所有分割句子的結果。使用的回溯函數爲: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