手撕练习之字符串
一、反转字符串中的每一个单词(leetcode 151、557)
思路:
1.利用string流输入将原字符串按空格分割,
2.然后去除尾部空格,
3.整体翻转字符串,
4.最后遍历字符串进行部分翻转。
class Solution {
public:
string reverseWords(string s) {
stringstream ss(s);
string token, str;
// 利用string流输入将原字符串按空格分割
while (ss >> token) {
str = str + token + " ";
}
// 检查字符串是否为空
if(str.empty())
return str;
// 去除尾部空格
int sz = str.size() - 1;
while (str[sz] == ' ') {
str.pop_back();
sz--;
}
// 先整体翻转
reverse(str.begin(), str.end());
for (int i = 0, j = 0; i <= str.size(); ++i){
// 部分翻转
if (i == str.size() || str[i] == ' '){
reverse(str.begin()+j, str.begin()+i);
j = i + 1;
}
}
return str;
}
};
二、多个字符串的最长公共前缀(leetcode 14)
思路:对于多个字符串,顺序比较每个字符串的字符是否相等,如果相等比较下一个字符;如果不相等则输出之前已比较相等的字符。
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if (!strs.size()) {
return "";
}
// 以第一个字符串为标杆
int length = strs[0].size();
int count = strs.size();
// 控制遍历字符串的元素
for (int i = 0; i < length; ++i) {
char c = strs[0][i];
// 控制遍历每个字符串
for (int j = 1; j < count; ++j) {
// 若某个字符串已遍历完 或 字符不相同 则输出
if (i == strs[j].size() || strs[j][i] != c) {
return strs[0].substr(0, i);
}
}
}
return strs[0];
}
};
三、N位数字串删除K个数字,使剩下的数字串最小(leetcode 402)
思路:尽可能让最高位小,最高位相同的情况下尽可能让次高位小,所以应该维护一个非递减栈。
1.构建一个非递减栈(单调栈思想):从左到右遍历数字num,依次进栈;每个数字进栈前检查是否比栈顶小,是的话弹掉栈顶,然后再循环跟栈顶比较,直到不比栈顶小或栈空了。出栈次数为K次。
2.如果K次(删数字)没用完,则依次弹出栈顶,删除剩下次数数字。
3.取出栈中数字,剔除前面的0.
class Solution {
public:
string removeKdigits(string num, int k) {
int len = num.size();
if(k<=0)
return num;
if(k>=len)
return "0";
vector<char> vt;
//构建一个非递减栈(单调栈思想):从左到右遍历数字num,依次进栈;每个数字进栈前检查是否
//比栈顶小,是的话弹掉栈顶,然后再循环跟栈顶比较,直到不比栈顶小或栈空了。出栈次数为K次。
for (auto x : num) {
while (!vt.empty() && vt.back() > x && k) {
vt.pop_back();
k--;
}
vt.push_back(x);
}
//如果K次(删数字)没用完,则依次弹出栈顶,删除剩下次数数字。
while(k>0){
vt.pop_back();
k--;
}
string res;
int i = 0;
//取出栈中数字,剔除前面的0.
for(;i<vt.size() && vt[i]=='0';++i);
for(;i<vt.size();++i){
res += vt[i];
}
if (res.empty())
return "0";
return res;
}
};
四、回文子串的个数(leetcode 647)
思路:分两种情况:
- 1.奇数长子回文串。以字符串中的每一个字符都当作回文串中间的位置,然后向两边扩散,每当成功匹配两个左右两个字符,结果count自增1,然后再比较下一对。
- 2.偶数长子回文串。如果是偶数长度的,那么i是最中间两个字符的左边那个,右边那个就是i+1,这样就能覆盖所有的情况。
class Solution {
public:
int countSubstrings(string s) {
int len = s.size();
int count = 0;
for(int i = 0;i<len;i++)
{
// 奇数长度的子回文串,中间元素相同后,左右分别向外扩一位再比较,
//这依次比较是否相同,不相同终止
int left = i,right = i;
while(left >= 0 && right < len && s[left] == s[right])
{
count++;
left--;
right++;
}
// 偶数长度的子回文串,中间俩元素相同后,左右分别向外扩一位再比较,
//i是最中间两个字符的左边那个,右边那个就是i+1
left = i;
right = i+1;
while(left >= 0 && right < len && s[left] == s[right])
{
count++;
left--;
right++;
}
}
return count;
}
};
五、最长无重复字符子串(leetcode 3)
思路:双指针加哈希,即每遍历一个元素就记录下它出现的次数,如果当前元素计数大于1,则向前找到与当前字符相同的字符,i记录的是这个相同字符的下一个字符,它们之间的区间长度就是无重复子串的长度,然后不断更新最大值。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> ump;
int maxlen = 0;
for (int i = 0, j = 0; j < s.size(); ++j){
// 每遍历一个元素就记录下它出现的次数
ump[s[j]]++;
// 向前找到与当前字符相同的字符,同时抹去相同字符前的字符计数
// i记录的是这个相同字符的下一个字符
while (ump[s[j]] > 1)
ump[s[i++]]--;
maxlen = max(maxlen, j - i + 1);
}
return maxlen;
}
};
六、最长回文子串(leetcode 5)
思路:以某一个字符为中心向两边扩散寻找回文子串,记录长度,最左端
- 如果子串是奇数,则以当前位置为中心,
- 如果子串是偶数,则以当前位置和下一个位置共同作为中心。
public:
string longestPalindrome(string s) {
int len = s.size();
int maxl = 0, start = 0;
for(int i = 0;i < len; i++)
{
// 如果子串是奇数,则以当前位置为中心
int l = i, r = i, lt = 0;
while(l >= 0 && r < len && s[l] == s[r])
{
l--;
r++;
}
lt = r-l-1;
if(lt > maxl)
{
maxl = lt;
start = l+1;
}
// 如果子串是偶数,则以当前位置和下一个位置共同作为中心
l = i; r = i+1; lt = 0;
while(l >= 0 && r < len && s[l] == s[r])
{
l--;
r++;
}
lt = r-l-1;
if(lt > maxl)
{
maxl = lt;
start = l+1;
}
}
return s.substr(start, maxl);
}
};
七、查找子串的位置(实现strStr())(leetcode 28)
class Solution {
public:
int strStr(string haystack, string needle) {
int lenh = haystack.size();
int lenn = needle.size();
for(int i = 0; i <= (lenh-lenn); i++)
{
int j = 0, it = i;
while(j < lenn && haystack[it] == needle[j])
{
j++;
it++;
}
// 如果j遍历到头了则说明needle是haystack的子串
if(j == lenn)
return i;
}
return -1;
}
};