44.反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s
的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
字符串的基本操作
class Solution {
public:
void reverseString(vector<char>& s) {
int n = s.size();
int left = 0,right = n-1;
while(left<right){
swap(s[left],s[right]);
left++;
right--;
}
}
};
541.反转字符串II
给定一个字符串 s
和一个整数 k
,从字符串开头算起,每计数至 2k
个字符,就反转这 2k
字符中的前 k
个字符。
- 如果剩余字符少于
k
个,则将剩余字符全部反转。 - 如果剩余字符小于
2k
但大于或等于k
个,则反转前k
个字符,其余字符保持原样。
直接模拟即可
class Solution {
public:
string reverseStr(string s, int k) {
for(int i = 0;i<s.size();i+=2*k){
if(i+k<=s.size()){
reverse(s.begin()+i,s.begin()+i+k);
}else{
reverse(s.begin()+i,s.end());
}
}
return s;
}
};
剑指Offer 05.替换空格
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1: 输入:s = "We are happy."
输出:"We%20are%20happy."
class Solution {
public:
string replaceSpace(string s) {
int count = 0; // 统计空格的个数
int sOldSize = s.size();
for (int i = 0; i < s.size(); i++) {
if (s[i] == ' ') {
count++;
}
}
// 扩充字符串s的大小,也就是每个空格替换成"%20"之后的大小
s.resize(s.size() + count * 2);
int sNewSize = s.size();
// 从后先前将空格替换为"%20"
for (int i = sNewSize - 1, j = sOldSize - 1; j < i; i--, j--) {
if (s[j] != ' ') {
s[i] = s[j];
} else {
s[i] = '0';
s[i - 1] = '2';
s[i - 2] = '%';
i -= 2;
}
}
return s;
}
};
方法:
很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。
这么做有两个好处:
- 不用申请新数组。
- 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
首先扩充数组到每个空格替换成"%20"之后的大小。
然后从后向前替换空格,也就是双指针法,过程如下:
i指向新长度的末尾,j指向旧长度的末尾。
151.翻转字符串里的单词
给你一个字符串 s
,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s
中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s
中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
方法1:栈
注意最后一个单词的入栈,以及最后一个空格的删除。
这种方法的时间复杂度是O(n),空间复杂度也是O(n)
class Solution {
public:
string reverseWords(string s) {
stack<string> st;
string ans;
string ss = "";
for (int i = 0; i < s.size(); i++) {
if (s[i] != ' ') {
ss += s[i];
}
else if (!ss.empty()) {
st.push(ss);
ss = "";
}
}
if (!ss.empty()) {
st.push(ss);
}
while (!st.empty()) {
string sss = st.top();
st.pop();
ans += sss + ' ';
}
if (!ans.empty()) {
ans.pop_back(); // Remove the trailing space
}
return ans;
}
};
方法2:
不要使用辅助空间,空间复杂度要求为O(1)。
我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。
所以解题思路如下:
- 移除多余空格
- 将整个字符串反转
- 将每个单词反转
注意:字符串中的erase函数本身就是O(n)的时间复杂度。
class Solution {
public:
string reverseWords(string s) {
// 反转整个字符串
reverse(s.begin(), s.end());
int n = s.size();
int idx = 0;
for (int start = 0; start < n; ++start) {
if (s[start] != ' ') {
// 填一个空白字符然后将idx移动到下一个单词的开头位置
if (idx != 0) s[idx++] = ' ';
// 循环遍历至单词的末尾
int end = start;
while (end < n && s[end] != ' ') s[idx++] = s[end++];
// 反转整个单词
reverse(s.begin() + idx - (end - start), s.begin() + idx);
// 更新start,去找下一个单词
start = end;
}
}
s.erase(s.begin() + idx, s.end());
return s;
}
};
剑指Offer58-II.左旋转字符串
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = "abcdefg", k = 2
输出: "cdefgab"
示例 2:
输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"
限制:
1 <= k < s.length <= 10000
方法1:双端队列
空间复杂度O(n)
class Solution {
public:
string reverseWords(string s,int k ) {
deque<char> deq;
string ans = "";
for (int i =0; i<s.size(); i++) {
deq.push_back(s[i]);
}
while (k--) {
char a = deq.front();
deq.pop_front();
deq.push_back(a);
}
while (!deq.empty()) {
ans += deq.front();
deq.pop_front();
}
return ans;
}
};
方法2:字符串反转
可以通过局部反转+整体反转 达到左旋转的目的。
具体步骤为:
- 反转区间为前n的子串
- 反转区间为n到末尾的子串
- 反转整个字符串
最后就可以达到左旋n的目的,而不用定义新的字符串,完全在本串上操作。
而且空间复杂度为O(1),时间复杂度为O(n),因为reverse函数的时间复杂度是O(n)
class Solution {
public:
string reverseLeftWords(string s, int n) {
reverse(s.begin(), s.begin() + n);
reverse(s.begin() + n, s.end());
reverse(s.begin(), s.end());
return s;
}
};
其实使用substr 和 反转 时间复杂度是一样的 ,都是O(n),但是使用substr申请了额外空间,所以空间复杂度是O(n),而反转方法的空间复杂度是O(1)。