字符串
1. 344.反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
class Solution {
public:
void reverseString(vector<char>& s) {
int left = 0;
int right = s.size() - 1;
while(left < right){
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
left++;
right--;
}
}
};
原地修改,直接双指针分别指向字符串两端,然后交换即可。
2. 541. 反转字符串II
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
class Solution {
public:
string reverseStr(string s, int k) {
int left,right;
for(int i = 0 ; i < s.size() ; i++){
if((i+1)%(2*k) == 0){
left = i + 1 - 2*k;
right = i - k;
while(left < right){
swap(s[left],s[right]);
left++;
right--;
}
}
}
if(s.size()%(2*k) < k){
left = s.size() - s.size()%(2*k);
right = s.size() - 1;
}else{
left = s.size() - s.size()%(2*k);
right = s.size() - 1 - (s.size()%(2*k) - k);
}
while(left < right){
swap(s[left],s[right]);
left++;
right--;
}
return s;
}
};
这题相当于是局部反转,判断好局部反转的起始位置和终止位置即可正确完成题目,主要是起始位置和终止位置的确定,得看好,索引从0开始,而个数从1开始,所以类似题最好能够带具体的值去检查一遍起始值和终止值。
3. 剑指Offer 05.替换空格
请实现一个函数,把字符串 s
中的每个空格替换成"%20"。
class Solution {
public:
string replaceSpace(string s) {
string result;
for(auto i : s){
if(i==' '){
result.push_back('%');
result.push_back('2');
result.push_back('0');
}else{
result.push_back(i);
}
}
return result;
}
};
// 方法二
class Solution {
public:
string replaceSpace(string s) {
int count = 0;
int initLength = s.size();
for(auto i : s){
if(i == ' ')
count++;
}
s.resize(initLength + count*2);
int left = initLength - 1;
int right = s.size() - 1;
while(left >= 0){
if(s[left] == ' '){
s[right--] = '0';
s[right--] = '2';
s[right--] = '%';
left--;
}else{
s[right--] = s[left--];
}
}
return s;
}
};
将c++中的string当成数组看就行了,不难。方法一是申请了额外的空间来保存新的字符串,方法二是直接修改的原字符串。先遍历字符串,求出空格的数量,然后对字符串进行扩容,然后从右往左进行遍历赋值,左指针初始位置为原字符串末尾,右指针为新字符串末尾。
其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作,这么做有两个好处:
不用申请新数组。
从后向前填充元素,避免了从前先后填充元素要来的 每次添加元素都要将添加元素之后的所有元素向后移动。
4. 151.翻转字符串里的单词
给你一个字符串 s ,逐个翻转字符串中的所有 单词 。单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。
说明:
- 输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
- 翻转后单词间应当仅用一个空格分隔。
- 翻转后的字符串中不应包含额外的空格。
class Solution {
public:
string reverseWords(string s) {
vector<string> vec;
string tmp;
for(auto i : s){
if(i == ' ' && tmp.empty()){
continue;
}else if(i == ' ' && !tmp.empty()){
vec.push_back(tmp);
tmp.clear();
}else{
tmp.push_back(i);
}
}
if(!tmp.empty())
vec.push_back(tmp);
string result;
for(int i = vec.size() - 1 ; i >=0 ; i--){
result += vec[i];
result += " ";
}
result.pop_back();
return result;
}
};
方法一解题其实还是有些许放水,因为本质上反转可以直接调用split,解法1我也是使用了额外的数组空间来做,总的来说就是将单词存在vector中,然后再还原到string中,整体思路比较简单。
5. 剑指Offer58-II.左旋转字符串
class Solution {
public:
string reverseLeftWords(string s, int n) {
// 两次反转字符串
int left = 0 , right = s.size() - 1;
while(left<right){
swap(s[left],s[right]);
left++;
right--;
}
left = 0, right = s.size() - n - 1;
while(left<right){
swap(s[left],s[right]);
left++;
right--;
}
left = s.size() - n, right = s.size() - 1;
while(left<right){
swap(s[left],s[right]);
left++;
right--;
}
return s;
}
};
通过多次局部旋转,达到目的。
6. 28. 实现 strStr()
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/implement-strstr
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.size(), m = needle.size();
for (int i = 0; i + m <= n; i++) {
bool flag = true;
for (int j = 0; j < m; j++) {
if (haystack[i + j] != needle[j]) {
flag = false;
break;
}
}
if (flag) {
return i;
}
}
return -1;
}
};
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.size()==0)
return 0;
int next[needle.size()];
getNext(next,needle);
int j = 0;
for(int i = 0 ; i < haystack.size() ; i++){
while(j > 0 && needle[j] != haystack[i]){
j = next[j - 1];
}
if(needle[j] == haystack[i])
j++;
if(j == needle.size())
return i - j + 1;
}
return -1;
}
void getNext(int * next , string needle){
int j = 0;
next[0] = j;
for(int i = 1 ; i < needle.size() ; i++){
while(j > 0 && needle[j] != needle[i]){
j = next[j - 1];
}
if(needle[j] == needle[i])
j++;
next[i] = j;
}
}
};
方法1为暴力解法,方法二为KMP解法。具体做法可以看我转载的博客。
7. 459.重复的子字符串
给定一个非空的字符串 s
,检查是否可以通过由它的一个子串重复多次构成。
class Solution {
public:
bool repeatedSubstringPattern(string s) {
if(s.size()==1||s.size()==0)
return false;
for(int i = 1 ; i <= s.size() / 2 ; i++){
if(s.size() % i == 0){
int count = s.size() / i - 1;
string tmp = s.substr(0,i);
string t = tmp;
while(count!=0){
tmp = tmp + t;
count--;
}
if(tmp==s){
return true;
}
}
}
return false;
}
};
class Solution {
public:
void getNext (int* next, const string& s){
next[0] = 0;
int j = 0;
for(int i = 1;i < s.size(); i++){
while(j > 0 && s[i] != s[j]) {
j = next[j - 1];
}
if(s[i] == s[j]) {
j++;
}
next[i] = j;
}
}
bool repeatedSubstringPattern (string s) {
if (s.size() == 0) {
return false;
}
int next[s.size()];
getNext(next, s);
int len = s.size();
if (next[len - 1] != 0 && len % (len - (next[len - 1] )) == 0) {
return true;
}
return false;
}
};
第一种暴力解法,直接暴毙超时,第二种是利用了KMP算法,先求出next前缀表数组,如果s字符串是满足题意的话,那么必然从第二个重复的字串开始next对应的元素就是不断累加的,1 2 3 4 5 6 7 8 ,所以用len % (len - (next[len - 1] ))
判断,如果len % (len - (next[len - 1])) == 0
,则说明 (数组长度-最长相等前后缀的长度) 正好可以被 数组的长度整除,说明有该字符串有重复的子字符串。数组长度减去最长相同前后缀的长度相当于是第一个周期的长度,也就是一个周期的长度,如果这个周期可以被整除,就说明整个数组就是这个周期的循环。(注:下图为next实现为-1时候的图片)【也就相当于是周期长度可以被next[len - 1]整除,保证了在字符串结束的时候进行了一个完整的周期。因为len = next[len - 1] + 周期长度,所以代码中直接使用len来除,其实用next[len - 1]来检查是否整除也是OK的】
如果截止到下图的2的位置(第二个f的位置),那么一个周期长度就是5,就不能被整除,就不符合题意。