【代码随想录】【LeetCode】自学笔记05 - 字符串 & 双指针

"本文总结了字符串处理中的关键技巧,包括时间复杂度分析、数组扩容、字符串反转、KMP算法、字符判断及转换、字符串筛选、华为机试策略等。强调了在处理字符串时注意结尾符'',以及在遍历和反转操作中双指针的应用。此外,还介绍了如何在有限空间内实现字符串反转、删除冗余空格和左旋效果。"
摘要由CSDN通过智能技术生成

总结

1、注意:根据下列实现函数的代码,记住常见函数的时间复杂度!例如reverse等
2、数组填充类的问题,可以先预先给数组扩容待填充后的大小,然后再从后向前进行操作:s.resize()
3、需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。
先整体反转再局部反转,反转字符串里的单词,空间复杂度O(1),空间复杂度O(n)。
先局部反转再整体反转,达到了左旋的效果,空间复杂度O(1),空间复杂度O(n)。
4、KMP:待填坑!!

华为机试技巧

  1. 在对字符串进行操作时,一定要记得结尾符’\0’,例如char a[10]中实际只能放入9个有用字符,最后一位需要放置结尾符。在字符串输入和for循环中经常会因为忽略了结尾符而出错。
    对于输入问题。后台输入均为字符串,未明确说明以“\n”结束的,用while(ch=’\n’)这样的写法可能会导致死循环机试的题目中并没有严格定义每行输入的结束符号是回车符或者EOF或者空格或者’\0’等,请按照机试题目要求,逐行读取输入,然后再对输入数据进行处理,不要采用循环逐个读取单个字符,否则可能会导致您本地测试通过,但是提交后系统返回运行超时或者答案错误。建议使用字符串保存输入,再做处理。不要使用’\n’、 ’\0’
  2. 字符操作:
    有很多机试题目需要对字符(不是字符串)进行判断和操作,这里列举一些常用的操作和编程技巧。
    判断一个字符是数字: if(c>=’0’ && c<=’9’);
    判断一个字符是大写字母: f(c>=’A’ && c<=’Z’);
    将char型数字转换为int型的一位数: int b = c - ‘0’;
    将int型数字转换为char型数字: char c = char(b + ‘0’);
    将大写字母转换为小写字母: char xiaoxie = daxie + (‘a’ - ‘A’);
  3. 字符串操作:
    依据条件筛选出一个长度不定的字符串:
int n = 0;
for(int i=0;i<n;i++){
	undefined
	if(/a[i]符合条件/){
		undefined
		b[n] = a[i];
		N++;
		}
	}
B[n] = ‘\0;

string.h库中的常用函数:
求字符串长度: int len = strlen(str);
字符串复制: strcpy();
字符串比较: strcmp();
字符串拼接: strcat();
查询字串: strchr();
查询子串: strstr();

字符串–>数字:int i = atoi(str);(如”136”)
数字–>字符串: itoa(str, i, 10);(第三个参数表示进制)

344. 反转字符串

反转链表中,使用了双指针的方法。
那么反转字符串依然是使用双指针的方法,只不过对于字符串的反转,其实要比链表简单一些。
因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。

    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--;
        }
        //或者用for,两个变量:
        for(int i = 0, j = s.size()-1; i<s.size()/2; i++,j--{//WRONG INT I INT J!
        swap(s[i], s[j]);
        }
      //  return;可有可无
    }

541.反转字符串II

务必记得string& s
主要思路:利用上一题构造的reverse函数及其三个参数+分类讨论:剩余字符少于 k 个,则将剩余字符全部反转。

class Solution {
public:
    string reverseStr(string s, int k) {
        for(int i = 0; i<s.size(); i+=2*k){
            if(s.size()-i+1 <= k) reverseStr11 (s, i, s.size()-1);
            else  reverseStr11(s, i, i+k-1);
        }
        return s;
    }
    void reverseStr11(string& s, int left, int right){
        for(int i = left, j = right; i<j; i++, j--){            swap(s[i], s[j]);        }
        return;
    }
};

剑指offer05.替换空格

char:隐藏有\0
string:可以用size() ;优先用;可以扩充大小:s.resize()

class Solution {
public:
    string replaceSpace(string s) {
        int oldSize = s.size();
        int count = 0;
        for(int i = 0; i<oldSize; i++){
            if(s[i]==' ') count++;
        }

        int newSize = oldSize + 2*count;
        s.resize(newSize);

        for(int i = oldSize-1, j = newSize-1; i<j; i--, j--){
            if(s[i] != ' ') s[j] = s[i];
            else{
                s[j] = '0';
                s[j-1] = '2';
                s[j-2] = '%';
                j = j-2;
            }
        }
        // char a[5] = "asd";
        // for (int i = 0; a[i] != '\0'; i++) {cout<<i<<" ";}      //0 1 2
        return s;  
    }

};

151. 颠倒字符串中的单词【需要再看】

知识点:
substr(start, lenth):复制子字符串,要求从指定位置开始,并具有指定的长度。

分析:

  1. 重点:resizesubstr 以及自己写的函数去掉首尾空格(参考数组章节的“27.移除元素”(该题也是一个重点题))、翻转部分字符串(两种情况,参见之前的541.反转字符串II)
  2. 真正重点有且仅有去除括号部分!!!!!!即,如何去掉首尾空格同时保证中间单词之间只有1个空格分隔。
    【代】代码见下,主要思想是只处理非空格部分,遇到单词最后一个字母就添加一个空格
    但是还是没看懂
//去除所有空格并在相邻单词之间添加空格, 快慢指针。
//整体思想参考https://programmercarl.com/0027.移除元素.html
//属实整不会了,又是for又是if又是while的
    string removeExtraSpaces(string& s){
        int l = 0;
        for(int r = 0; r<s.size(); ++r){
            if(s[r]!=' '){//遇到非空格就处理,即删除所有空格。
                if(l!=0){//手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
                    s[l]=' ';
                    l++;
                }
                while(r<s.size()&&s[r]!=' '){ //补上该单词,遇到空格说明单词结束。 //这直接和for条件一样是在干啥            
                    s[l]=s[r];
                    l++;
                    r++; 
                }
            }
        }
        s.resize(l);//slow的大小即为去除多余空格后的大小。
        return s;
    }

整体代码如下

class Solution {
public:
    void reverse(string& s, int L, int R){
        while(L<R) {
            swap(s[L], s[R]);
            L++;R--;
        }
        return ;
    }
//去除所有空格并在相邻单词之间添加空格, 快慢指针。
//整体思想参考https://programmercarl.com/0027.移除元素.html
//属实整不会了,又是for又是if又是while的
    string removeExtraSpaces(string& s){
        int l = 0;
        for(int r = 0; r<s.size(); ++r){
            if(s[r]!=' '){//遇到非空格就处理,即删除所有空格。
                if(l!=0){//手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
                    s[l]=' ';
                    l++;
                }
                while(r<s.size()&&s[r]!=' '){ //补上该单词,遇到空格说明单词结束。 //这直接和for条件一样是在干啥            
                    s[l]=s[r];
                    l++;
                    r++; 
                }
            }
        }
        s.resize(l);//slow的大小即为去除多余空格后的大小。
        return s;
    }

    string reverseWords(string s) {
        int L = 0;
        int R = 0;
        s = removeExtraSpaces(s);
        reverse(s, 0, s.size()-1);
        int j = 0;
        for(int i = 0; i<=s.size(); i++){
            if(s[i]==' ' || i==s.size()){
                reverse(s, j, i-1);
                j=i+1;
            }
        }
        return s;
    }
    // //自己写的,有错误,见下
    // string removeExtraSpaces(string& s){
    //     cout<<"s.size="<<s.size();
    //     int L = 0;
    //     int R = 0;
    //     while(R<s.size()){
    //         if(R+1<s.size() && s[R]==' ' && s[R+1]==' '){
    //             R++;
    //         }
    //         else {
    //             s[L] = s[R];
    //             L++;
    //             R++;
    //         }
    //     }
    //     L1=0;
    //     while(s[L1]==' ') L1++;
    //     R = s.size()-1;
    //     while(s[R]==' ') R--;
    //     string subs = s.substr(L, R-L+1);
    //     return subs;       
    //     /*why?
    //     input "  the sky is    blue   "
    //     output" the sky is blue lue   "
    //     */
    // }
};

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

知识点:

  1. 几个自带的,有【翻转功能】的函数:
    swap函数 ( T& a, T& b )
    reverse函数反转**[first, last)区间的数据
    sort函数对
    [first, last)**区间的函数进行排序
  2. 其中,C++自带的 reverse函数用法:
    只能用 (s.begin() + n, s.end())或者(str. begin(), str. end())这样,用地址表示参数,不可以用s[0]表示参数!!!!
    最后这个参数是末项再+1,也就是 [ 左开右闭区间 )

分析:
为了让本题更有意义,提升一下本题难度:不能申请额外空间,只能在本串上操作。
使用整体反转+局部反转,就可以实现局部反转的目的。

    void reverse(string& s, int l, int r){
            while(l<r){
                swap(s[l], s[r]);
                l++;
                r--;
            }
            return;
    }
    string reverseLeftWords(string s, int n) {
        reverse(s, 0, n-1);
        reverse(s, n, s.size()-1);
        reverse(s, 0, s.size()-1);
        return s;
    }

KMP(尚未)

KMP的经典思想就是:当出现字符串不匹配时,可以记录一部分之前已经匹配的文本内容,利用这些信息避免从头再去做匹配。

28. 实现 strStr()
459.重复的子字符串

双指针

数组,链表和字符串中很常用。
双指针法展现出效率的优势:通过两个指针在一个for循环下完成两个for循环的工作。
除了链表一些题目一定要使用双指针,其他题目都是使用双指针来提高效率,一般是将O(n^2)的时间复杂度,降为 O ( n ) O(n) O(n)

数组篇
其实很多数组填充类的问题,都可以先预先给数组扩容待填充后的大小,然后再从后向前进行操作

字符串篇
反转字符串:
定义两个指针(也可以说是索引下标),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。时间复杂度是O(n)。

填充字符串:
不要额外辅助空间了,首先扩充数组到每个空格替换成"%20"之后的大小。然后双指针从后向前替换空格。
again:其实很多数组(字符串)填充类的问题,都可以先预先给数组扩容待填充后的大小,然后从后向前进行操作。

删除冗余空格:
使用双指针法O(n)就可以搞定。一定要注意for循环下用erase的情况,一般可以用双指针写效率更高!

链表篇
翻转链表:
是现场面试,白纸写代码的好题,考察了候选者对链表以及指针的熟悉程度,而且代码也不长,适合在白纸上写。
使用双指针法来翻转链表,只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表。

在链表中求环:
使用快慢指针(双指针法),分别定义 fast 和 slow指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

N数之和篇
通过前后两个指针不断向中间逼近,在一个for循环下完成两个for循环的工作。
四数之和,其实思路是一样的,在三数之和的基础上再套一层for循环,依然是使用双指针法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值