2.力扣2018年常见编程题总结(字符串)

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 (前缀树),包含 insertsearch, 和 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;
}

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值