string容器训练算法题
文章目录
- 题单链接
- 验证回文串
- 不借助任何函数接口
- 代码
- tolower转成小写直接判断
- 代码
- 最后一个单词的长度
- 思路
- 代码
- 字符串中的第一唯一字符
- 思路
- 代码
- 字符串相加
- 思路
- 代码
- 仅仅反转字母
- 思路
- 代码
文章内容:
- 验证回文串
- 最后一个单词的长度
- 字符串中的第一个唯一字符
- 字符串相加
- 仅仅反转字母
简介:该篇文章就是拿几个算法题为string的常用接口与模拟实现练练手,题目还是比较简单的,这些思路与代码也是自己写的,可能有点啰嗦一般般望见谅,主要是与各位好友分享分享自己的看法,熟悉熟悉容器string以及一些tolower或者isalpha函数的用处。
题单链接
力扣string算法题单 – 点链接可以直接进行总练习,也可点题目链接做一题练一题
验证回文串
给定一个字符串 s ,验证 s 是否是 回文串 ,只考虑字母和数字字符
,可以忽略字母的大小写。
本题中,将空字符串定义为有效的 回文串 。
示例 1:
输入: s = “A man, a plan, a canal: Panama”
输出: true
解释:“amanaplanacanalpanama” 是回文串
不借助任何函数接口
- 先写一个判断是不是大小写字母和数字字符的函数为了让双指针从头尾遍历找到数字或字母字符,还需注意的一点是:
left < right
这个极为忽视的条件,进入下面判断之前也得判断是否满足
bool isletter_num(char ch) {return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') ||(ch >= 'A' && ch <= 'Z');
}int left = 0, right = s.size() - 1;while (left < right && !isletter_num(s[left])) left++;
while (left < right && !isletter_num(s[right])) right--;// 找到有效字符就停下来,去判断
- 因为大小写字母的ASCII码值相差32,
a ~ z:97 ~ 122;A ~ Z:65 ~ 90
,那也无需去判断s[left],s[right]谁是大小写字母,若满足s[left] - 32 == s[right] || s[left] + 32 == s[right] || s[left] == s[right]
就没问题,left++,right–。但存在一种特殊情况:0的ASCII码值为48,p的为80
,那两者相减也是32,但这并不是回文串,这种数字字符判断相等也得特殊处理
if (s[left] - 32 == s[right] || s[left] + 32 == s[right] || s[left] == s[right])
{left++;right--;
}
else return false;
- 单独写一个数字字符的函数,
先在判断大小写字母之前
把数字字符拎出来单独判断,如果s[left],s[right]
其中是数字字符,只需要判断s[left] == s[right]
即可
bool isnum(char ch) { return (ch >= '0' && ch <= '9'); }
if (isnum(s[left]) || isnum(s[right]))
{if (s[left++] != s[right--]) return false;
}
代码
class Solution {bool isletter_num(char ch) {return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') ||(ch >= 'A' && ch <= 'Z');}bool isnum(char ch) { return (ch >= '0' && ch <= '9'); }public:bool isPalindrome(string s) {int left = 0, right = s.size() - 1;while (left < right) {while (left < right && !isletter_num(s[left]))left++;while (left < right && !isletter_num(s[right]))right--;if (left < right) {if ((isnum(s[left]) || isnum(s[right]))) {if (s[left++] != s[right--])return false;} else {if (s[left] - 32 == s[right] || s[left] + 32 == s[right] ||s[left] == s[right]) {left++;right--;} elsereturn false;}}}return true;}
};
tolower转成小写直接判断
这里也可以用tolower函数将字符串中的大写字母直接转换为小写字母,但islower函数用不上,因为题目中规定了字符也包含了数字字符,因此得自己去写一个判断有效字符的函数,仍然是利用双指针头尾找到有效字符,不过还是比上面代码简洁方便的
代码
class Solution {bool isletter_num(char ch) {return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z');}
public:bool isPalindrome(string s) {// 先遍历一遍s将里面的字符转成小写for(int i = 0; i < s.size(); i++){s[i] = tolower(s[i]);}// 转成小写后就能直接判断是否相等,但仍然得去找有效字符int left = 0, right = s.size() - 1;while (left < right){while (left < right && !isletter_num(s[left])) left++;while (left < right && !isletter_num(s[right])) right--;if (left < right){if (s[left] == s[right]){left++;right--;}else{return false;}}}return true;}
};
最后一个单词的长度
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1:
输入:s = “Hello World”
输出:5
解释:最后一个单词是“World”,长度为 5。
思路
可以利用string的rfind
的接口从string的末尾遍历找到第一个字母后,再接着去找后面的空格,两者之间就是最后一个单词,但可能最后一个单词其末尾仍然有空格即"hello world "
,那现在就能用到isalpha
函数,单词alphabetic:字母的
,那现在就没有问题了
代码
class Solution {
public:int lengthOfLastWord(string s) {int i = s.size() - 1;while (!isalpha(s[i])) i--; // 找到第一个从最后出现的字符size_t it = s.rfind(' ', i);return i - it;}
};
字符串中的第一唯一字符
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。
示例 1:
输入: s = “leetcode”
输出: 0
思路
这里可以借助哈希数组,这题题目规定s中全部是小写字母,那就可以开辟一个大小为26的数组hash,遍历字符串s将其(s[i] - 'a'
)作为hash数组的键(下标),而统计s[i]
个数作为值(hash[s[i]-'a']++
)计入到数组hash对应下标,最后遍历数组hash,访问其值,第一次出现为1的值其对应的键就是第一个唯一的字符。当然这里也能用到容器map
,不过这用时效率挺感人的
代码
// 借助哈希数组
class Solution {
public:int firstUniqChar(string s) {int haxi[26] = {0}; // 得初始化成0for (auto& ch : s) haxi[ch - 'a']++;for (int i = 0; i < s.size(); i++){if (haxi[s[i] - 'a'] == 1) return i;}return -1;}
};// 借助容器map
class Solution {
public:int firstUniqChar(string s) {map<char, int> hash;for (auto& ch : s) hash[ch - 'a']++;for (int i = 0; i < s.size(); i++){if (hash[s[i] - 'a'] == 1) return i;}return -1;}
};
字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。
示例 1:
输入:num1 = “11”, num2 = “123”
输出:“134”
思路
大体思路就是用双指针cur1去标记num1的最后一个字符,cur2去标记num2的最后一个字符,创建一个string对象来放两者的和,再分别取出字符-'0'
相加后尾插到该string对象(sum.push(相加的数 + '0')
),但有些细节情况需要去处理
- 可能num1与num2的长度不一样,但两者必须都走完整个字符循环相加插入才能结束,
提前结束的那就相当于加个0
int val1 = cur1 >= 0 ? num1[cur1--] - '0' : 0;
int val2 = cur2 >= 0 ? num2[cur2--] - '0' : 0;
- 可能有(
'9' + '5'
)需要考虑进位问题,用个变量next去计算进位,变量count用来计算两者的和再加上进位next(int count = val1 + val2 + next
),那尾插的数就是它除10的余数,商就是它的进位
//开始尾插到s中int count = val1 + val2 + next;int ret = count % 10;next = count / 10;sum.push_back(ret + '0');// 插入的是字符,别忘记了
- 边界情况,可能最后一次插入后退出循环其中next = 1,比如(
"999" + "1"
)插入三个0后,最后的进位没处理,那就直接sum.push_back( 1 + '0')
,其次在插入到string中用的都是尾插,因此得去对string逆置
// 处理边界情况,next = 1;if (next == 1) sum.push_back(1 + '0');// 将s直接逆置reverse(sum.begin(), sum.end());
代码
class Solution {
public:string addStrings(string num1, string num2) {// 先做好准备工作string sum;// 这里提前开辟空间,避免多次扩容sum.reserve(max(num1.size(), num2.size()) + 1);int next = 0; //考虑到进制的问题,定义个nextint cur1 = num1.size() - 1, cur2 = num2.size() - 1;while (cur1 >= 0 || cur2 >= 0){//先把cur1 和 cur2 的值给提取出来int val1 = cur1 >= 0 ? num1[cur1--] - '0' : 0;int val2 = cur2 >= 0 ? num2[cur2--] - '0' : 0;//开始尾插到s中int count = val1 + val2 + next;int ret = count % 10;next = count / 10;sum.push_back(ret + '0');// 插入的是字符,别忘记了}// 处理边界情况,next = 1;if (next == 1) sum.push_back(1 + '0');// 将s直接逆置reverse(sum.begin(), sum.end());return sum;}
};
仅仅反转字母
给你一个字符串 s ,根据下述规则反转字符串:
所有非英文字母保留在原有位置。
所有英文字母(小写或大写)位置反转。
返回反转后的 s 。
示例 1:
输入:s = “ab-cd”
输出:“dc-ba”
思路
它的思路就是利用双指针从左右两边遍历分别找到英文字母然后直接交换即可,这里也可以用到isalpha
函数
代码
class Solution {
public:string reverseOnlyLetters(string s) {int left = 0, right = s.size() - 1;while (left < right){while(left < right && !isalpha(s[left])) left++;while(left < right && !isalpha(s[right])) right--;swap(s[left++], s[right--]);}return s;}
};