声明:题目来源于LeetCode,解法来源于剑指offer以及LeetCode上官方以及大佬们的思路,我只是进行了一点小小的搬运工作以及整理注释。
最长不重复子字符串
题目描述:请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
来源:LeetCode 3 && 剑指offer 48
面试:腾讯
思路:滑动窗口
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int size = s.size();
if(size==0) return 0;
int start = 0, end = 0, res = 0, length = 0;
unordered_map<char,int> map;
while(end<size){
char temp = s[end];
if(map.find(temp)!=map.end() && map[temp]>=start){
start = map[temp]+1;
length = end - start;
}
map[temp] = end; //存pari<char, int>
end++;
length++;
res = max(res, length);
}
return res;
}
};
- 优化
之前使用了unordered_map实现快速查找,底层是哈希散列表。可以用vector来代替hashmap,利用ACSII码来记录字符位置,而出现位置为vec[(int)char]的值。没出现时设为-1,出现后给与一个正值。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int size = s.size();
if(size==0) return 0;
int start = 0, end = 0, res = 0, length = 0;
vector<int> vec(128,-1);
while(end<size){
char temp = s[end]
if(vec[(int)temp]>=start){
start = vec[(int)temp]+1;
length = end-start;
}
vec[(int)temp] = end;
end++;
length++;
res = max(res, length);
}
return res;
}
};
字符串压缩
题目描述:字符串压缩。利用字符重复出现的次数,编写一种方法,实现基本的字符串压缩功能。比如,字符串aabcccccaaa
会变为a2b1c5a3
。若“压缩”后的字符串没有变短,则返回原先的字符串。你可以假设字符串中只包含大小写英文字母(a至z)。
题目来源:LeetCode 01.06
面试:暂无
思路:顺序遍历,记录次数
易错点:要考虑压缩后长度是否变短!
class Solution {
public:
string compressString(string s) {
int size = s.size();
if(size==0) return s;
char temp = s[0];
int cnt = 1;
string res="";
for(int i=1;i<size;++i){
if(temp=s[i]) cnt++;
else{
res += temp + to_string(cnt);
cnt = 1;
temp = s[i];
}
}
res += temp + to_string(cnt);
int size_new = res.size();
return size_new<size ? res:s;
}
};
表示数值的字符串
题目描述:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、“0123"及”-1E-16"都表示数值,但"12e"、“1a3.14”、“1.2.3”、"±5"及"12e+5.4"都不是。
题目来源:LeetCode 65 && 剑指offer 20
面试:暂无
思路:先除去空格;根据e|E划分指数底数;再判断指数和底数(小数点和正负号的数目)是否合理
易错点:在用substr时,一定要注意边界是否写错!!!
class Solution {
public:
bool isNumber(string s) {
int i = s.find_first_not_of(' '); //返回第一个不是' '的位置
if(i==string::npos) return false; //没找到返回npos,通常为-1;但是不要当作-1来比较
int j = s.find_last_not_of(' '); //返回最后一个不是' '的位置
s = s.substr(i, j-i+1); //从字符i开始,跨越j-i+1个字符的长度,进行拷贝
//判断指数和底数是否符合
int e = s.find('e');
if(e==string::npos) return checkNoexp(s);
else return checkNoexp(s.substr(0,e)) && checkexp(s.substr(e+1));
}
//判断底数:正负号出现在首位;小数点只出现一次;不能出现0~9以外的符号
bool checkNoexp(string str){
bool res = false, point = false;
int n = str.size();
for(int i=0;i<n;++i){
if(str[i]=='+' || str[i]=='-'){
if(i!=0) return false;
}
else if(str[i]=='.'){
if(point) return false;
point = true;
}
else if(str[i]<'0' || str[i]>'9')
return false;
else
res = true;
}
return res;
}
//判断指数:正负号出现在首位;没有0~9以外的符号
bool checkexp(string str){
bool res = false;
int n = str.size();
for(int i=0;i<n;++i){
if(str[i]=='+' || str[i]=='-'){
if(i!=0) return false;
}
else if(str[i]<'0' || str[i]>'9')
return false;
else
res = true;
}
return res;
}
};
正则表达式匹配
题目描述:请实现一个函数用来匹配包含.
和*
的正则表达式。模式中的字符.
表示任意一个字符,而*
表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"
与模式"a.a"
和"ab*ac*a"
匹配,但与"aa.a"
和"ab*a"
均不匹配。
题目来源:LeetCode 10 && 剑指offer 19
面试:暂无
思路:递归
易错点:使用substr()的前提是判断字符串是否为空!!
class Solution {
public:
bool isMatch(string s, string p) {
if(p.empty()) return s.empty(); //p是pattern,s是目标字符串
//不为空时,必然存在p[1],若p[1]为*,情况如下:
//1) p[0]!=s[0],p[1]之前可认为是0个字符,比较s和p[1]之后的pattern;
//2) p[0]==s[0] || p[0]=='.',比较s[1]以后的和p
if(p[1]=='*'){
return isMatch(s,p.substr(2)) ||
(!s.empty() && (s[0]==p[0] || p[0]=='.')) &&
isMatch(s.substr(1),p);
//如果p[1]!='*',那么情况有两种:
//1) s[0]==p[0] || p[0]=='.',这样在0处匹配,比较下标为1以后的s和p
//2) s和p在0处不匹配,返回false (这里的返回false可以省略,因为||运算符前者的true/false即可决定最终结果
else{
return (!s.empty() && (s[0]==p[0] || p[0]=='.') && isMatch(s.substr(1), p.substr(1))) || false;
}
};
最长回文串
题目描述:给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。在构造过程中,请注意区分大小写。比如 "Aa"
不能当做一个回文字符串。
题目来源:LeetCode 409
面试:暂无
思路:可以用桶来实现统计出现奇数次数字符个数即可
备注:非常巧妙,来自官方题解评论区用户 “总有一天丶”
class Solution {
public:
int longestPalindrome(string s) {
vector<int> vec(128,0); //常用ACSII码个数
int cnt = 0; //统计奇数字符个数
for(auto c:s) vec[c]++;
for(auto i:vec) cnt += i%2;
return cnt==0 ? s.size():(s.size()-cnt+1);
}
};
字符串的排列
题目描述:输入一个字符串,打印出该字符串中字符的所有排列。你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
题目来源:LeetCode 38
面试:暂无
思路:每次固定一个位置的数,进行递归处理,回溯思想;降重通过判断交换位置之间是否有重复
class Solution {
public:
vector<string> res;
vector<string> permutation(string s) {
int size = s.size();
permutate(s,0,size-1);
return res;
}
void permutate(string &s, int start, int end){
if(start==end){
res.push_back(s);
return;
}
for(int i=start;i<=end;++i){
if(judge(s,start,i)) continue;
swap(s[start],s[i]);
permutate(s,start+1,end);
swap(s[start],s[i]);
}
}
//如果[start,pos]范围中有和s[pos]相等的,说明pos这个字符不用交换到第一个字符
//第一个字符实现所有不重复字符的可能性
bool judge(string s,int start, int pos){
for(int i=start;i<pos;++i){
if(s[i]==s[pos]) return true;
}
return false;
}
};
电话号码的组合
题目描述:给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
题目来源:LeetCode 17
面试:暂无
思路:回溯或者称深搜
class Solution {
public:
vector<string> letterCombinations(string digits) {
unordered_map<char, string> table{
{'0'," "}, {'1',"*"}, {'2',"abc"},
{'3',"def"}, {'4',"ghi"}, {'5',"jkl"},
{'6',"mno"}, {'7',"pqrs"}, {'8',"tuv"},
{'9',"wxyz"}
};
vector<string> res;
if(digits.size()==0) return res;
dfs(res,"",table,digits,0);
return res;
}
void dfs(vector<string> &res, string str, unordered_map<char,string> table, const string& digits, int k){
if(str.size()==digits.size()){
res.push_back(res);
return;
}
string temp = table[digits[k]];
for(auto s:temp){
str+=s;
dfs(res,str,table,digits,k+1);
str.pop_back();
}
}
};
Z字形变换
题目来源:LeetCode 6
面试:暂无
思路:判断方向变量根据所在行进行调整
易错点:暂无
class Solution {
public:
string convert(string s, int numRows) {
int size = s.size();
if(numRows==1) return s;
//每一行为空字符串,总共numRows行
vector<string> rows(numRows,"");
int curRow = 0;
bool goDown = false;
for(int i=0;i<size;++i){
rows[curRow] += s[i];
//当到达0行和末行时,调整方向
if(curRow==0 || curRow==numRows-1) goDown = !goDown;
curRow += goDown==true ? 1:-1;
}
string res;
for(auto str:rows){
res+=str;
}
return res;
}
};
最长有效括号
题目描述:给定一个只包含'('
和')'
的字符串,找出最长的包含有效括号的子串的长度。
题目来源:LeetCode 32
面试:暂无
思路:栈
class Solution{
public:
int longestValidParentheses(string s) {
int size = s.size();
stack<int> st;
//把左括号下标存在栈中,碰上右括号时pop掉顶层,再减去顶层的值即为括号长度
st.push(-1);
int len = 0;
for(int i=0;i<size;++i){
if(s[i]=='(') st.push(i);
else if(s[i]==')'){
st.pop(); //因为有一个初始值,所以不用担心第一次碰上右括号st为空
if(st.empty()) st.push(i); //st为空时,更新起始坐标
else len = max(len,i-st.top());
}
}
return len;
}
};
反转单词顺序
题目描述:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串"I am a student. “,则输出"student. a am I”。
来源:剑指offer 58-I && LeetCode 151
思路:两次翻转,利用一个翻转操作的函数
易错点:在反转子字符串时,如何保存临时的字符串?最后一次存的子字符串是否翻转后的?(第二次写时没想到)
class Solution {
public:
string reverseWords(string s) {
int size = s.size();
if(size<=1) return s;
int lhs=0, rhs=size-1;
//去掉左右两端的空格
while(s[lhs]==' ') lhs++;
while(s[rhs]==' ') rhs--;
//先翻转子字符串
string temps="", res="";
for(int i=lhs;i<=rhs;++i){
//保存子字符串
if(s[i]!=' ') temps += s[i];
else{
while(s[i]==' ') i++;
i--; //循环体结束后要先+1再判断,所以这里i减1
Reverse(temps);
res += temps;
res += " ";
temps="";
}
}
Reverse(temps); //这里容易遗忘
res += temps;
Reverse(res);
return res;
}
private:
void Reverse(string &str){
int size = str.size();
int end = size-1, start = 0;
while(start<end){
char temp = str[start];
str[start++] = str[end];
str[end--] = temp;
}
}
};