1.给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
解:首先对字符串去掉标点符号以及全部变为小写。去掉标点符号采用isalnum函数,该函数用来判断字符是否为数字或字母。将字母变为小写采用tolower函数,之后根据对称中心判断两边的字符是否一样,每个都相同则代表是回文串。
#include
#include
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
#include
#include
#include
using namespace std;
class Solution {
public:
void DFS(string s,int start,vector& out, vector>&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> partition(string s) {
vector> res;
vector out;
DFS(s, 0, out, res);//采用深度优先搜索
return res;
}
bool isPalindrome(string s, int start, int end)//回文判断
{
while (start> 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
#include
#include
#include
#include
using namespace std;
class Solution {
public:
bool wordBreak(string s, vector& wordDict) {
int len = s.size();//字符串长度
vector 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 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
#include
#include
#include
#include
using namespace std;
class Solution {
public:
vector wordBreak(string s, vector& wordDict) {
vector dp(s.size() + 1, false);
dp[0] = true;
unordered_set 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 res;
vector each;
DFS(s, res, each, m);
return res;
}
void DFS(string s, vector& res, vector& each, unordered_set &m) {
if (s.empty()) {
string tmp;
for (vector::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 wordDict{ "cats", "dog", "sand", "and", "cat" },res;
res = s1.wordBreak("catsanddog", wordDict);
for (int i = 0; i < res.size(); i++)
{
cout<< res[i]<
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
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")<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
#include
#include
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 findWords(vector >& board, vector& words) {
vector res;
if (words.empty() || board.empty() || board[0].empty()) return res;
vector > visit(board.size(), vector(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 > &board, TrieNode *p, int i, int j, vector > &visit, vector &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> board = { {'o','a','a','n'},
{'e','t','a','e'},
{'i','h','k','r' },
{'i','f','l','v'} };
vectorwords = { "oath","pea","eat","rain" },re;
re = S.findWords(board, words);
for (int i = 0; i < re.size(); i++)
{
cout <
7.给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram" 输出: true
示例 2:输入: s = "rat", t = "car" 输出: false
说明:你可以假设字符串只包含小写字母。
解:字母异位词即两个字符串中的字母集一样,个数一样,只是顺序不一样,因此可以用散列表来做,统计第一个字符串中的每个字母出现的次数,对第二个字符串做同样的操作,两个统计结果若一样则代表他们字母集合是一样的。
代码:
#include
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<
8.给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
案例:s = "leetcode" 返回 0.
s = "loveleetcode", 返回 2.
解:同样利用散列的性质,对字符串统计出现的次数依次存放在res数组中,取出来的第一个为1的对应索引即为第一次出现的字母。
代码:
#include
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<
9.编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[]
的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:输入:["h","e","l","l","o"] 输出:["o","l","l","e","h"]
解:因为题目要求用O(1)的额外空间来存储中间变量,依次可以将待翻转的字符串进行首尾的交换,用一个for循环完成
#include
using namespace std;
class Solution {
public:
void reverseString(vector& 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 a={ 'h','e','l','p'};
s1.reverseString(a);
return 1;
}