【代码随想录】字符串 (KMP,BF ,扩充数组,for循环固定规律一段处理,整体反转+局部反转,双指针,滑动窗口,库函数)

344. 反转字符串

题目
思路:双指针
官方:

class Solution {
public:
    void reverseString(vector<char>& s) {
        int n = s.size();
        for (int left = 0, right = n - 1; left < right; ++left, --right) {
            swap(s[left], s[right]);
        }
    }
};

随想录:

void reverseString(vector<char>& s) {
    for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {
        swap(s[i],s[j]);
    }
}

注意
数组中双指针的停止:可以是left<s.length()/2,也可以是left<right

541. 反转字符串 II

这道题目耗时很长,原因主要是越界问题,以及最后剩余字符如何处理,根据测试用例一遍一遍改的,因为这种方法不能考虑周全。
启示:
本题对比上两道题目给反转加上了一些条件,当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章
我的代码:

class Solution {
public:
      string reverse( string s){
        for(int i=0,j=s.size()-1;i<j;i++,j--)
        {
            swap(s[i],s[j]);
        }
        return s;
      }
    string reverseStr(string s, int k) {
        // int j,i; 想写就地反转呢
        // for( j=0;j<s.size();){
        // for( i=j;i<j+k&&i<s.size();i++){       
        //     swap(s[i],s[k-i-1]);
        // }
        // j=i+k-1;
        if(s.size()<k)
           return reverse(s);
        string str="";
        int i;
        for( i=0;i<s.size();i+=(2*k)){   
             str+=reverse(s.substr(i,k));  //因为前面判断了s.size()一定大于k,所以这里肯定不会越界
            if((i+2*k)<=s.size())
                str+=s.substr(i+k,k);
        }
        if(i!=s.size()&&(i-k)<s.size())  //最后for循环跳出是i+2*k,跳回到最后剩余不到k的位置上,再将这些加到str上,前面没有i!=size()的判断,则用例:abcd这种就会是bacdcd多出来了
           str+=s.substr(i-k);
        return str;
    }
  //0123      
};

改进点:

  1. 自己写反转函数,可以直接使用引用,传入开始,结尾的参数,不用设置str消耗了多余空间。空间复杂度为O(n),而官方直接引用就是O(1)
  2. 可以使用是string库函数中的反转函数:reverse(s.begin(),s.end())
  3. 使用截至字符串函数:substr(startpos, length);

官方代码:

class Solution {
public:
    string reverseStr(string s, int k) {
        int n = s.length();
        for (int i = 0; i < n; i += 2 * k) {
            reverse(s.begin() + i, s.begin() + min(i + k, n));
        }
        return s;
    }
};

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/reverse-string-ii/solution/fan-zhuan-zi-fu-chuan-ii-by-leetcode-sol-ua7s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

剑指 Offer 05. 替换空格

题目

官方思路:
本题目是将一个字符换成三个字符,使用char类型的数组,该数组长度为原来数组的三倍(最大全替换),最后再返回为真实长度的数组
我的代码:

class Solution {
public:
    string replaceSpace(string s) {
        string str="";
        int j=0;
        for(int i=0;i<s.size();i++){
            if(s[i]==' '){
                str+="%20";
                continue;  //这里可以用continue,也可以不用在下面用else
            }
             str+=s[i];//string可以直接加char类型的,效率更高
           // str.push_back(s[i]);
        }
    return str;
    }
};

看了随想录后自己写的
bug点:
最后返回截取的开始位置
冗余点:
其实可以先计算空格的个数,不用直接扩充三倍,见随想录

class Solution {
public:
    string replaceSpace(string s) {
        int n=s.size(),j;
        s.resize(s.size()*3);
        int i=s.size()-1;
        for(j=n-1;j>=0;j--){
           if(s[j]!=' ')
           {
               s[i--]=s[j];
           }else{
               s[i]='0';
               s[i-1]='2';
               s[i-2]='%';
               i=i-3;
           }
       }
       return s.substr(i+1);
    }
};

随想录:双指针
精妙之处在于双指针的终止条件是i==j相等,因为扩充的是空格的个数,所以相当于i在追j,当追上之后说明空格已经被填满了,前面没有空格了,可以停止,而不是i>=0作为i停止条件。

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;
    }
};

知识点:

  1. char转成string类型方式:str.push_back©; str+=c;
  2. s.resize(newsize) //扩充s的大小到newsize

151. 颠倒字符串中的单词

题目

我的思路:
使用vector存储单词,再逆序加到一个string上
bug点

  1. 前面有空格和后面有空格的处理
  2. 最后一个单词str的处理
class Solution {
public:
    string reverseWords(string s) {
        vector<string> v;
        string ss,str;
        int k=0;
        while(s[k]==' ')
            k++;
        for(int i=k;i<s.size();i++){
            if(s[i]==' '&&s[i-1]!=' '){
              //  reverse(str.begin(),str.end());
                v.push_back(str);
                   str="";
            }else if(s[i]!=' '){ //如果直接写else则,因为上面if条件的判断还是会加入空字符
                str+=s[i];
            }
        }
        if(str!="")
           v.push_back(str);//最后一个单词
        for(int i=v.size()-1;i>0;i--){
            ss+=v[i]+" ";
        }
        ss+=v[0];
        return ss;

    }
};

看了随想录后的
算法思路:

  1. 先移除空格
  2. 反转整个字符串
  3. 反转单个单词

bug点:
临界值问题:最后一个元素的处理【单词】

  • 产生临界值问题的原因:因为每次以一个标志来判断是否是满足条件的,这里是每个单词后面的空格为标志,所以特殊情况就是最后一个单词后面没有空格时怎么办

计算数组下标的问题:

  • reverse(),erase()这类要通过数组下标来对数据做处理的库函数!
class Solution {
public:
//双指针移除会造成单词的次序改变!!!所以用王道的课后题删除数组元素方法二,使用相对位置来删除数组的元素,即插入排序的思想,k始终指向最后一个储存好的元素

   string remove(string s){//引用会改变最后的字符大小吗,应该不会,因为要删除,所以字符大小会改变,不像reverse仅仅是反转,所以可以用&直接返回string
       int k=0,j=0;
       while(s[j]==' ')
           j++;
       for(int i=j;i<s.size();i++){
         if((s[i]==' '&&s[i-1]!=' '&&i!=s.size()-1)||s[i]!=' ') //这里不需要判断i>0,虽然有s[i-1]但是在前面已经将最前面的空格跳过去了,所以i要么是字母,要么是新的空格;  如何去掉最后单词后面的空格,
       {
            s[k]=s[i];
            k++;
         }  
       }
       if(s[s.size()-1]==' ')
              return s.substr(0,k-1);
        else return s.substr(0,k);
   }
    string reverseWords(string s) {
        int j=0,pos;
         s=remove(s);
         reverse(s.begin(),s.end());
        while(s[j]==' '){
             j++;
        }
        pos=j;//记录空格后面的那个位置
        for(int i=j;i<s.size();i++){
            if(s[i]==' '){
                reverse(s.begin()+pos,s.begin()+i);//反转上一个空格的下一个字符到本次空格的上一个字符之间的单词  ;只是反转了,但是空格却没消失,这里每次遇到空格后反转的是空格前面的一个单词,而且如果两个单词中间有多个空格,就会重复反转
                pos=i+1;
            }
        }
        reverse(s.begin()+pos,s.end()); //最后一个后面没有空格
        // if(s[0]==' ')
        //  s.erase(0,1);  //我懂了比如abc空空  倒数第二个空格就没覆盖住!
        return s;
    }
};
//难点如何消除空格,除了s.erase(),只能是覆盖;以及反转后最后一个单词后面如果没有空格,那么该单词将不能反转
//随想录的解决方法是:先消除多余的空格,再反转整个单词,再反转单个单词。
//如何消除空格?!!!这个和王道删除值等于x的那个题目一样,和移除元素那个力扣题目一样!!!!!所以这个题目是综合的,你要做的就是先理清题目的要求,再分解步骤,每个步骤对应一个小算法或者经验值思路,正确将步骤做好就可以!!!!
//还有就是每次的bug点你要记住才有效啊!!每次都是临界点!不能全部依靠力扣的测试去看,要用自己的脑子模拟!!
//不成熟的算法是漏洞百出,各种补,成熟的算法应该是清晰且整齐的,覆盖所有的情况

反思
关于删除数组元素:

  1. 使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到O(n)的时间复杂度。【随想录】
  2. 使用插入排序的思想
  3. 相对位置王道
  4. 双指针(这种会改变数组元素的位置)

关于删除空格:
随想录方法一:
随想录使用的是&,因为最后用了resize()重新定义了大小,所以可以用引用!如果不resize()则返回的还是原来的字符串大小

// 移除冗余空格:使用双指针(快慢指针法)O(n)的算法
    void removeExtraSpaces(string& s) {
        int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
        // 去掉字符串前面的空格
        while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
            fastIndex++;
        }
        for (; fastIndex < s.size(); fastIndex++) {
            // 去掉字符串中间部分的冗余空格
            if (fastIndex - 1 > 0
                    && s[fastIndex - 1] == s[fastIndex]
                    && s[fastIndex] == ' ') {
                continue;
            } else {
                s[slowIndex++] = s[fastIndex];
            }
        }
        //这里和我写的一样!!
        if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
            s.resize(slowIndex - 1);
        } else {
            s.resize(slowIndex); // 重新设置字符串大小
        }
    }

随想录方法二:这种方法是每次给单词后面手动加入空格。

 void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
        int slow = 0;   //整体思想参考Leetcode: 27. 移除元素:https://leetcode-cn.com/problems/remove-element/
        for (int i = 0; i < s.size(); ++i) { //
            if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
                if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
                while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
                    s[slow++] = s[i++];
                }
            }
        }
        s.resize(slow); //slow的大小即为去除多余空格后的大小。
    }

随想录方法:
使用了一个标志来看是否进入了单词区,我使用的是pos标记第一个单词字符位置,用i标志单词的最后一个字符,没有使用标志进入单词区。使用标志每次if都判断。我的方法和随想录的简介版本一样 随想录解法
关于删除空格中&问题:使用resize()解决
关于最后一个单词没有空格的问题:也是单独处理最后一个单词

// 版本一
class Solution {
public:
    // 反转字符串s中左闭又闭的区间[start, end]
    void reverse(string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }

    // 移除冗余空格:使用双指针(快慢指针法)O(n)的算法
    void removeExtraSpaces(string& s) {
        int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
        // 去掉字符串前面的空格
        while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
            fastIndex++;
        }
        for (; fastIndex < s.size(); fastIndex++) {
            // 去掉字符串中间部分的冗余空格
            if (fastIndex - 1 > 0
                    && s[fastIndex - 1] == s[fastIndex]
                    && s[fastIndex] == ' ') {
                continue;
            } else {
                s[slowIndex++] = s[fastIndex];
            }
        }
        if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
            s.resize(slowIndex - 1);
        } else {
            s.resize(slowIndex); // 重新设置字符串大小
        }
    }

    string reverseWords(string s) {
        removeExtraSpaces(s); // 去掉冗余空格
        reverse(s, 0, s.size() - 1); // 将字符串全部反转
        int start = 0; // 反转的单词在字符串里起始位置
        int end = 0; // 反转的单词在字符串里终止位置
        bool entry = false; // 标记枚举字符串的过程中是否已经进入了单词区间
        for (int i = 0; i < s.size(); i++) { // 开始反转单词
            if (!entry) {
                start = i; // 确定单词起始位置
                entry = true; // 进入单词区间
            }
            // 单词后面有空格的情况,空格就是分词符
            if (entry && s[i] == ' ' && s[i - 1] != ' ') {
                end = i - 1; // 确定单词终止位置
                entry = false; // 结束单词区间
                reverse(s, start, end);
            }
            // 最后一个结尾单词之后没有空格的情况
            if (entry && (i == (s.size() - 1)) && s[i] != ' ' ) {
                end = i;// 确定单词终止位置
                entry = false; // 结束单词区间
                reverse(s, start, end);
            }
        }
        return s;
    }
    
    // 当然这里的主函数reverseWords写的有一些冗余的,可以精简一些,精简之后的主函数为:
    /* 主函数简单写法
    string reverseWords(string s) {
        removeExtraSpaces(s);
        reverse(s, 0, s.size() - 1);
        for(int i = 0; i < s.size(); i++) {
            int j = i;
            // 查找单词间的空格,翻转单词
            while(j < s.size() && s[j] != ' ') j++;
            reverse(s, i, j - 1);
            i = j;
        }
        return s;
    }
    */
};

官方解答 双端队列 API方法

官方解答

剑指 Offer 58 - II. 左旋转字符串

题目链接

方法一(随想录):局部反转+整体反转
我写的:整体反转+局部反转
冗余点:可以先局部,再整体反转更简洁

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        reverse(s.begin(),s.end());
        reverse(s.begin(),s.begin()+s.size()-n);
        reverse(s.begin()+s.size()-n,s.end());
        return s;
    }
};

随想录:

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;
    }
};

方法二:使用额外空间s,累加

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        string ss="";
        for(int i=n;i<s.size();i++){
            ss+=s[i];
        }
        for(int i=0;i<n;i++){
            ss+=s[i];
        }
        return ss;

    }
};

方法三:substr拼接

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        return s.substr(n)+s.substr(0,n);
    }
};

28.实现strstr()

KMP算法

459.重复的子字符串

暴力法:我写的。。。。。

class Solution {
public:
    bool repeatedSubstringPattern(string s) {
    //字符串匹配的问题,这里的规律就是循环出现,如果没有循环出现
    if(s=="")
      return false;
    string str="";
    int j;
    for(int i=0;i<s.size();i++){
        str+=s[i];
        int n=str.size();
        for(j=n;j<s.size();j=j+n){
             if(s.substr(j,n)!=str){
                   break;
             }
        }
        if(j==s.size()&&n!=s.size()) //当n为s.size()时,就不是子串了
          return true;
    }
    return false;
    }
};

滑动窗口

3. 无重复字符的最长子串

库函数

reseize
s.substr()
reverse

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值