1.給定一個字符串,驗證它是否是迴文串,只考慮字母和數字字符,可以忽略字母的大小寫。
說明:本題中,我們將空字符串定義爲有效的迴文串。
示例 1:
輸入: "A man, a plan, a canal: Panama"
輸出: true
解:首先對字符串去掉標點符號以及全部變爲小寫。去掉標點符號採用isalnum函數,該函數用來判斷字符是否爲數字或字母。將字母變爲小寫採用tolower函數,之後根據對稱中心判斷兩邊的字符是否一樣,每個都相同則代表是迴文串。
#include <iostream>
#include<string>
using namespace std;
class Solution {
public:
bool isPalindrome(string s) {
int len = s.size();
char *p=new char[len];
int j = 0;
for (int i = 0; i < len; i++)
{
if (isalnum(s[i]))//判斷是否爲字面或數字
{
p[j] = tolower(s[i]);
++j;
}
}
for (int i = 0; i < j/2; i++)
{
if (p[i] ==p[j - i-1])
continue;
else
return 0;
}
return 1;
}
};
int main()
{
Solution s1;
cout << s1.isPalindrome(" A man, a plan, a canal: Panama");//("A man, a plan, a canal: Panama");
}
2.給定一個字符串 s,將 s 分割成一些子串,使每個子串都是迴文串。
返回 s 所有可能的分割方案。
示例:
輸入: "aab" 輸出: [ ["aa","b"], ["a","a","b"] ]
解:採用回溯法的思想,利用深度優先進行搜索,res存儲最終結果,out存儲臨時結果,通過遞歸調用DFS完成深度搜索,傳入字符串,以及開始的第0層。在DFS函數中,遞歸的中止條件爲遍歷字符串的起始位置=字符串的長度,將臨時結果壓入res棧中。從i=start位置開始,進行循環直到等於字符串的長度,這裏可以看作爲每次取字符串的start位置處開始判斷是否爲迴文,i表示移動的位置,即最開始從start處開始移動。循環體中:判斷start到i處的字符串是否爲迴文,是的話則截取start到i長度的字符串壓入out臨時結果變量中,再次遞歸調用DFS,此時應該判斷從i+1處開始後面的字符串是否爲迴文,此時又進入新的for循環。最後將結果壓入res後,需要將out結果彈出,裝入下一次的臨時結果。
#include <iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
public:
void DFS(string s,int start,vector<string>& out, vector<vector<string>>&res)
{
//遞歸中止條件
if (start ==s.size())
{
res.push_back(out);//彈出臨時結果到res中
return;
}
for ( int i = start; i < (int)s.size(); i++)
{
if (isPalindrome(s, start,i))//判斷是否爲迴文
{
out.push_back(s.substr(start, i - start + 1));//壓入截取的字符串
DFS(s, i + 1, out, res);//start位置處開始判斷後面的字符串
out.pop_back();
}
}
}
vector<vector<string>> partition(string s) {
vector<vector<string>> res;
vector<string> out;
DFS(s, 0, out, res);//採用深度優先搜索
return res;
}
bool isPalindrome(string s, int start, int end)//迴文判斷
{
while (start<end)
{
if (s[start] != s[end])
return false;
++start;
--end;
}
return true;
}
};
int main()
{
Solution s1;
vector<vector<string>> result= s1.partition("aba");
for (int i = 0; i < (int)result.size(); i++)
{
for (int j = 0; j < (int)result[i].size(); j++)
{
cout << result[i][j]<<" ";
}
cout << endl;
}
}
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"
。
解:通過兩個for循環遍歷字符串的字符,建立狀態數組存放對應字符串的狀態
#include <iostream>
#include<unordered_set>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
int len = s.size();//字符串長度
vector<bool> state(len + 1, false);
state[0] = true;
string tmp;
for (int i = 1; i <= len; i++)
{
for (int j = 0; j <= i; j++)
{
//判斷前面j爲是否爲true,並且判斷從j到j+i-j-1處的字符串是否在字典中
tmp = s.substr(j, i - j);
//begin是vector的第一個單詞,end爲vector最後一個單詞的下一個
if (state[j] && find(wordDict.begin(), wordDict.end(), tmp) != wordDict.end()) {
state[i] = true;
}
}
}
return state[len];//0-len處的字符串是否在字典中
}
};
int main()
{
Solution s1;
vector<string> wordDict{ "apple", "pen" };
cout<< s1.wordBreak("applepenapple", wordDict);
}
4.給定一個非空字符串 s 和一個包含非空單詞列表的字典 wordDict,在字符串中增加空格來構建一個句子,使得句子中所有的單詞都在詞典中。返回所有這些可能的句子。
說明:分隔時可以重複使用字典中的單詞。你可以假設字典中沒有重複的單詞。
示例 1:
輸入: s = "catsanddog
" wordDict = ["cat", "cats", "and", "sand", "dog"]
輸出: [ "cats and dog", "cat sand dog" ]
解:
#include <iostream>
#include<unordered_set>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
class Solution {
public:
vector<string> wordBreak(string s, vector<string>& wordDict) {
vector<bool> dp(s.size() + 1, false);
dp[0] = true;
unordered_set<string> m(wordDict.begin(), wordDict.end());
int maxLength = 0;
for (int i = 0; i < (int)wordDict.size(); i++) {
maxLength = std::max(maxLength, (int)wordDict[i].size());
}
for (int i = 1; i <= s.size(); i++) {
for (int j = std::max(i - maxLength, 0); j < i; j++) {
if (dp[j] && m.find(s.substr(j, i - j)) != m.end()) {
dp[i] = true;
break;
}
}
}
vector<string> res;
vector<string> each;
DFS(s, res, each, m);
return res;
}
void DFS(string s, vector<string>& res, vector<string>& each, unordered_set<string> &m) {
if (s.empty()) {
string tmp;
for (vector<string>::iterator it = each.begin(); it != each.end(); it++) {
if (it != (each.end() - 1)) {
tmp = tmp + *it + " ";
}
else {
tmp = tmp + *it;
}
}
res.push_back(tmp);
return;
}
for (int i = 0; i < s.size(); i++) {
string str = s.substr(0, i + 1);
if (m.find(str) != m.end()) {
each.push_back(str);
DFS(s.substr(i + 1), res, each, m);
each.pop_back();
}
}
}
};
int main()
{
Solution s1;
vector<string> wordDict{ "cats", "dog", "sand", "and", "cat" },res;
res = s1.wordBreak("catsanddog", wordDict);
for (int i = 0; i < res.size(); i++)
{
cout<< res[i]<<endl;
}
}
5.實現一個 Trie (前綴樹),包含 insert
, search
, 和 startsWith
這三個操作。
示例:
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 true
trie.search("app"); // 返回 false
trie.startsWith("app"); // 返回 true
解:構建一個前綴樹,樹的根結點爲root,每個結點含有26個子結點分佈代表26個字母,首先是初始化這26個子節點爲空,若插入一個單詞,則在對應索引處創建一個新的結點,來代表單詞的第一個字母,對於第二個字母同樣在第一個字母結點下面創建26個子節點,在第二個字母對應索引處創建新的子節點用來代表第三個字母。索引未被使用的都爲空,使用了的存放的是下一個結點的地址。一個單詞結束後val值加一。
#include <iostream>
using namespace std;
class Node
{
public:
int val=0; //一個單詞的完結val加一
Node* next[26]; //指針數組,每個數的結點有26個子節點來存放一個字母
Node()
{
//初始化指針數組
for (int i = 0; i < 26; i++)
{
next[i] = NULL;
}
}
};
class Trie {
public:
Node* root;
Trie()
{
root = new Node();//返回一個結點對象指針
}
void insert(string word) {
int len = word.length();
Node* p = root;
for (int i = 0; i < len; i++)
{
if (p->next[word[i]-'a']==NULL)//判斷頭字母在樹中是否爲空
{
p->next[word[i] - 'a'] = new Node();//創建一個結點,該結點由root引申下來的
}
p = p->next[word[i] - 'a'];//指向下一個結點
}
p->val++;
}
bool search(string word) {
int len = word.length();
Node* find = root;
for (int i = 0; i < len; i++)
{
if (find->next[word[i] - 'a'] == NULL)
{
return false;
}
find = find->next[word[i] - 'a'];
}
if (find->val != 0)
return true;
else
return false;
}
bool startsWith(string prefix) {
int len = prefix.length();
Node* find = root;
for (int i = 0; i < len; i++)
{
if (find->next[prefix[i]-'a']==NULL)
{
return false;
}
find = find->next[prefix[i] - 'a'];
}
return true;
}
};
int main()
{
Trie *obj = new Trie(); //創建一個前綴樹對象
obj->insert("apple"); //插入字符串
cout << obj->search("apple")<<endl; //查找字符串
cout << obj->startsWith("app"); //查找字符串
}
6.給定一個二維網格 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"]
解:
#include <iostream>
#include<vector>
#include<string>
using namespace std;
class Solution {
public:
struct TrieNode {
TrieNode *child[26];
string str;
TrieNode() : str("") {
for (auto &a : child) a = NULL;
}
};
struct Trie {
TrieNode *root;
Trie() : root(new TrieNode()) {}
void insert(string s) {
TrieNode *p = root;
for (auto &a : s) {
int i = a - 'a';
if (!p->child[i]) p->child[i] = new TrieNode();
p = p->child[i];
}
p->str = s;
}
};
vector<string> findWords(vector<vector<char> >& board, vector<string>& words) {
vector<string> res;
if (words.empty() || board.empty() || board[0].empty()) return res;
vector<vector<bool> > visit(board.size(), vector<bool>(board[0].size(), false));
Trie T;
for (auto &a : words) T.insert(a);
for (int i = 0; i < board.size(); ++i) {
for (int j = 0; j < board[i].size(); ++j) {
if (T.root->child[board[i][j] - 'a']) {
search(board, T.root->child[board[i][j] - 'a'], i, j, visit, res);
}
}
}
return res;
}
void search(vector<vector<char> > &board, TrieNode *p, int i, int j, vector<vector<bool> > &visit, vector<string> &res) {
if (!p->str.empty()) {
res.push_back(p->str);
p->str.clear();
}
int d[][2] = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} };
visit[i][j] = true;
for (auto &a : d) {
int nx = a[0] + i, ny = a[1] + j;
if (nx >= 0 && nx < board.size() && ny >= 0 && ny < board[0].size() && !visit[nx][ny] && p->child[board[nx][ny] - 'a']) {
search(board, p->child[board[nx][ny] - 'a'], nx, ny, visit, res);
}
}
visit[i][j] = false;
}
};
int main()
{
Solution S;
vector<vector<char>> board = { {'o','a','a','n'},
{'e','t','a','e'},
{'i','h','k','r' },
{'i','f','l','v'} };
vector<string>words = { "oath","pea","eat","rain" },re;
re = S.findWords(board, words);
for (int i = 0; i < re.size(); i++)
{
cout <<re[i] << endl;
}
}
7.給定兩個字符串 s 和 t ,編寫一個函數來判斷 t 是否是 s 的字母異位詞。
示例 1:
輸入: s = "anagram", t = "nagaram" 輸出: true
示例 2:輸入: s = "rat", t = "car" 輸出: false
說明:你可以假設字符串只包含小寫字母。
解:字母異位詞即兩個字符串中的字母集一樣,個數一樣,只是順序不一樣,因此可以用散列表來做,統計第一個字符串中的每個字母出現的次數,對第二個字符串做同樣的操作,兩個統計結果若一樣則代表他們字母集合是一樣的。
代碼:
#include <iostream>
using namespace std;
class Solution {
public:
bool isAnagram(string s, string t) {
int res1[26] = { 0 };
int res2[26] = { 0 };
for (int i = 0; i < (int)s.length(); i++)
res1[int(s[i] - 'a')] += 1;
for (int i = 0; i < (int)t.length(); i++)
res2[int(t[i] - 'a')] += 1;
for (int i = 0; i < 26; i++)
{
if (res1[i]-res2[i]==0)
continue;
else
return 0;
}
return 1;
}
};
int main()
{
Solution s1;
cout<<s1.isAnagram("rat", "cat");
return 1;
}
8.給定一個字符串,找到它的第一個不重複的字符,並返回它的索引。如果不存在,則返回 -1。
案例:s = "leetcode" 返回 0.
s = "loveleetcode", 返回 2.
解:同樣利用散列的性質,對字符串統計出現的次數依次存放在res數組中,取出來的第一個爲1的對應索引即爲第一次出現的字母。
代碼:
#include <iostream>
using namespace std;
class Solution {
public:
int firstUniqChar(string s) {
int res[26] = { 0 };
for (int i = 0; i < (int)s.length() ; i++)
{
res[(int)(s[i] - 'a')] += 1; //res存放每個字母的統計次數
}
for (int j = 0; j < (int)s.length(); j++)
{
if (res[(int)(s[j] - 'a')] == 1) //最先出現的一定爲第一次出現的字母
return j;
}
return -1;
}
};
int main()
{
Solution s1;
cout<<s1.firstUniqChar("leetcode");
return 1;
}
9.編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[]
的形式給出。不要給另外的數組分配額外的空間,你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。你可以假設數組中的所有字符都是 ASCII 碼錶中的可打印字符。
示例 1:輸入:["h","e","l","l","o"] 輸出:["o","l","l","e","h"]
解:因爲題目要求用O(1)的額外空間來存儲中間變量,依次可以將待翻轉的字符串進行首尾的交換,用一個for循環完成
#include <iostream>
using namespace std;
class Solution {
public:
void reverseString(vector<char>& s) {
char temp;
for (int i = 0; i < (int)s.size()/2; i++)
swap(s[i], s[s.size() - i-1]);
}
};
int main()
{
Solution s1;
vector<char> a={ 'h','e','l','p'};
s1.reverseString(a);
return 1;
}