[TOC]
一、分割字符串
1816. 截断句子(简单难度)
class Solution {
public:
/*解析:分割+截断+拼接
最简单的思路是先分割字符串为单词数组,然后反转数组元素,再拼接单词并添加上新的空格,
该思路的难点是根据空格分割字符串为单词数组。
*/
string truncateSentence(string s, int k) {
// 分割字符串为字符串数组
vector<string> words=splitWord(s);
// 截断字符串数组
vector<string> temp(words.begin(),words.begin()+k);
// 拼接为一个字符串
string res;
for(int i=0;i<temp.size();i++) {
if(i==0)res+=temp[i];
else{
res+=" ";
res+=temp[i];
}
}
return res;
}
vector<string> splitWord(string str){
vector<string> words;
string word;
for(char c:str){
// 遇到空格字符并且word有字符就添加word并且重置word,保证不会添加开头空格
if(c==' '&&word.size()>0){
words.push_back(word);
word="";
}
// 不添加空格
else if(c!=' '){
word+=c;
}
}
// str末位不一定有空格
if(word.size()) words.push_back(word);
return words;
}
};
151. 翻转字符串里的单词(中等难度)
剑指 Offer 58 - I. 翻转单词顺序(简单难度)
方法一:分割+反转+拼接
class Solution {
public:
/*解析:这道题是以空格分隔单词,难点在于开头、中间、结尾的空格数量都不确定。
方法一:分割+反转+拼接
最简单的思路是先分割字符串为单词数组,然后反转数组元素,再拼接单词并添加上新的空格,
该思路的难点是根据空格分割字符串为单词数组。
*/
string reverseWords(string s) {
// 分割字符串为字符串数组
vector<string> words=splitWord(s);
// 反转字符串数组
reverse(words.begin(),words.end());
// 拼接为一个字符串
string res;
for(int i=0;i<words.size();i++) {
if(i==0)res+=words[i];
else{
res+=" ";
res+=words[i];
}
}
return res;
}
vector<string> splitWord(string str){
vector<string> words;
string word;
for(char c:str){
// 遇到空格字符并且word有字符就添加word并且重置word,保证不会添加开头空格
if(c==' '&&word.size()>0){
words.push_back(word);
word="";
}
// 不添加空格
else if(c!=' '){
word+=c;
}
}
// str末位不一定有空格
if(word.size()) words.push_back(word);
return words;
}
};
方法二:原地双指针
class Solution {
public:
/*解析:这道题是以空格分隔单词,难点在于开头、中间、结尾的空格数量都不确定。
方法二:原地双指针(不容易想到)
因为该方法需要反转,所以从后向前遍历:
left,right都从最后一个元素出发,right先走到第一个字符处,left=right,走到第一个空格处
[left+1,right]就是单词所在索引区间,循环重复
*/
string reverseWords(string s) {
string res;// 结果字符串
int n = s.size();
if(n == 0) return res;
int left =n-1;// 左指针
int right = n - 1;// 右指针
while(right >= 0){
//从后往前寻找第一字符(如果不是空且没有到头,right就左移)
while(right >= 0 && s[right] == ' ') right--;
if(right < 0) break;
//从后往前寻找第一个空格
int left = right;
while( left >= 0 && s[left] != ' ' ) left--;
//添加单词到结果,单词所在位置[left+1,right]
res += s.substr(left + 1, right - left);
res += ' ';
//继续往前分割单词
right = left;
}
//去除最后一个字符空格
if (!res.empty()) res.pop_back();
return res;
}
}
186. 翻转字符串里的单词 II(中等难度)
三、旋转字符串
剑指 Offer 58 - II. 左旋转字符串(简单难度)
class Solution {
public:
/*解析:本题的意思是将字符串的[0,n)移动到字符串的末尾
方法一:列表遍历拼接(借助额外内存空间):先拼接[n,s.length()),再拼接[0,n)
方法二:列表遍历拼接(取余操作简化)
方法三:3次反转:反转[0,n);反转[n,s.length());反转[0,s.length())
*/
string reverseLeftWords(string s, int n) {
//方法一:列表遍历拼接(借助额外内存空间)
// string res;
// // 获取[n,s.length())加入res中
// for(int i=n;i<s.length();i++){
// res+=s[i];
// }
// // 获取[0,n)加入res中
// for(int i=0;i<n;i++){
// res+=s[i];
// }
// return res;
//方法二:列表遍历拼接(取余操作简化)
// string res;
// int len=s.length();
// for(int i=n;i<n+len;i++){
// res+=s[i%len];
// }
// return res;
//方法三:巧妙反转
reverse(s.begin(),s.begin()+n);
reverse(s.begin()+n,s.end());
reverse(s.begin(),s.end());
return s;
}
};
796. 旋转字符串(简单难度)
class Solution {
public:
/*本题是判断s是否能够通过多次左旋转来得到goal
我们已经知道左旋转字符串的操作(剑指 Offer 58 - II. 左旋转字符串)
那么可以通过模拟左旋转来判断,左旋转的次数是有限的:1~s.length()
*/
bool rotateString(string s, string goal) {
// 旋转1~s.length()次
for(int n=1;n<=s.length();n++){
// 左旋转1个字符
reverse(s.begin(),s.begin()+1);
reverse(s.begin()+1,s.end());
reverse(s.begin(),s.end());
// 检查本次旋转后是否==goal
if(s==goal) return true;
}
return false;
}
};
三、字符串比较大小
剑指 Offer 45. 把数组排成最小的数(中等难度)
class Solution {
public:
/*解析:本题是一个排序问题,但难点在于如何比较2个数?
小的数排在前面不一定数就小,例如3和30,330>303.
解决方案是将整数转成字符串,这样排序a和b两个数时,可以比较a+b和b+a谁更大
在代码实现时需要知道如何实现自定义排序方式
*/
// 自定义排序方式
static bool cmp(string& a,string& b){
return a + b < b + a;
}
string minNumber(vector<int>& nums) {
// 将整数数组转换为字符串数组
vector<string> strs;
for(int num:nums) strs.push_back(to_string(num));
// 自定义排序
sort(strs.begin(), strs.end(), cmp);
// 将字符串数组拼接为字符串
string res;
for(string s : strs) res+=s;
return res;
}
};