c++刷算法【字符串】

温习数据结构与算法,准备一些比赛,为了学业和就业,以及提升自己的编程能力,将系统的刷刷算法,入手c++跟着carl,leetcode刷题笔记将持续更新…

string就是只能用来存放char的vector,但它和vector相比,额外提供了很多操作字符串的接口函数。

与Java的String对比:在Java文档中将String类对象称为不可变字符串,Java字符串中的字符是不能修改的。这一点和C++不同,C++中字符串时可以修改的。

在C语言中,把一个字符串存入一个数组时,也把结束符 '\0’存入数组,并以此作为该字符串是否结束的标志。

例如这段代码:

char a[5] = "asd";
for (int i = 0; a[i] != '\0'; i++) {
}

C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用’\0’来判断是否结束。

遍历

string a = "asd";
for (int i = 0; i < a.size(); i++) {
}
for (auto iter = v1.begin(); iter != s1.end();++iter) {
	cout << *iter ;		// 输出当前字符
}

for (auto it : s1) {
	cout << it ;		// 输出当前字符
}

// 如果想用范围for语句改变容器中的元素值,请加上引用
for (auto &it : s1) {
	cout << it ;		// 输出当前字符
}
image-20220313105340997

库函数要会使用,也要知道其原理

库函数
构造函数
string` `str;    ``//生成一个空字符串
string` `str (``"ABC"``) ``//等价于 str="ABC"<br>
string` `str (``"ABC"``, strlen) // 将"ABC"存到str里,最多存储前strlen个字节
string` `s(``"ABC"``,stridx,strlen)  //将"ABC"的stridx位置,做为字符串开头,存到str里.且最多存储strlen个字节.
string` `s(strlen, ``'A'``) ``//存储strlen个'A'到str里
成员函数
str1.length()//获取字符串长度 
str1.size();            //获取字符串数量,等价于length()
str1.capacity();          //获取容量,容量包含了当前string里不必增加内存就能使用的字符数
str1.resize(10);           //表示设置当前string里的串大小,若设置大小大于当前串长度,则用字符\0来填充多余的.
str1.resize(10,char c);     //设置串大小,若设置大小大于当前串长度,则用字符c来填充多余 
str1.empty();    //判断为空, 为空返回true

str.reverse(str.begin(),str.end());  //反转
substr()子串
str.substr(3); //返回 [3] 及以后的子串
str.substr(2, 4); //返回 str[2]~str[2+(4-1)] 子串(即从[2]开始4个字符组成的字符串)

//拷贝
str2=str1.substr(2);        //提取子串,提取出str1的下标为2到末尾,给str2
str2=str1.substr(2,3);     //提取子串,从 str1的下标为2开始,提取3个字节给str2
 
const char *s1= str.data();   //将string类转为字符串数组,返回给s1
char *s=new char[10];
str.copy(s,count,pos);    //将str里的pos位置开始,拷贝count个字符,存到s里.
replace()替换
str.replace(2, 4, "sz");//返回把 [2]~[2+(4-1)] 的内容替换为 "sz" 后的新字符串
str.replace(2, 4, "abcd", 3);//返回把 [2]~[2+(4-1)] 的内容替换为 "abcd" 的前3个字符后的新字符串
insert()插入
str.insert(2, "sz");//从 [2] 位置开始添加字符串 "sz",并返回形成的新字符串
str.insert(2, "abcd", 3);//从 [2] 位置开始添加字符串 "abcd" 的前 3 个字符,并返回形成的新字符串
str.insert(2, "abcd", 1, 3);//从 [2] 位置开始添加字符串 "abcd" 的前 [2]~[2+(3-1)] 个字符,并返回形成的新字符串
追加赋值

除了用重载的 + 操作符,还可以使用函数来完成。

str.push_back('a');//在 str 末尾添加字符'a'
str.append("abc");//在 str 末尾添加字符串"abc"

/*assign() :赋值函数 ,里面会重新释放分配字符串内存 */
str1.assign("HELLO");                   //str1="HELLO"
str1.assign("HELLO", 4);                //str1="HELL" ,只保留4个字符
str1.assign("HELLO", 2, 3);             //str1="LLO"    ,从位置2开始,只保留3个字符
str1.assign(5, 'c');                    //str1="CCCCC"             //按字符赋值
erase()删除
str.erase(3);//删除 [3] 及以后的字符,并返回新字符串
str.erase(3, 5);//删除从 [3] 开始的 5 个字符,并返回新字符串
str1.clear();              //删除所有
find()
/*查找成功返回位置,查找失败,则n等于-1*/
str.find("ab");      //返回字符串 ab 在 str 的位置
str.find("ab", 2);    //在 str[2]~str[n-1] 范围内查找并返回字符串 ab 在 str 的位置
str.find("CDEfg",1,3);   //从位置1处,查找"CDEfg"的前3个字符
str.rfind("ab", 2);   //在 str[0]~str[2] 范围内查找并返回字符串 ab 在 str 的位置

str.find_first_of("apple", 2);//返回 apple 中任何一个字符首次在 str[2]~str[n-1] 范围中出现的位置
str.find_first_not_of("apple", 2);//返回除 apple 以外的任何一个字符在 str[2]~str[n-1] 范围中首次出现的位置
str.find_last_of("apple", 2);//返回 apple 中任何一个字符最后一次在 str[0]~str[2] 范围中出现的位置
sort()
  1. 要求输入一个范围[first, last)

  2. 随机迭代器,能用此算法的容器是支持随机访问的容器:vector, deque, string。

    sort(v.begin(),v.end()); // 对 vector 排序

    反向迭代器 sort(v.rbegin(),v.rend())

    • 升序:sort(begin,end,less());
    • 降序:sort(begin,end,greater()).

    sort(vs,vs + 2); // 对数组排序

自己编写一个比较函数来实现,接着调用三个参数的sort:sort(begin,end,compare)就成了。

对于list容器,把compare作为sort的参数就可以了,即:sort(compare).

1)自己编写compare函数:

bool compare(int a,int b){
      return a<b;   //升序排列,如果改为return a>b,则为降序
}

快排qsort():
qsort ( 数组名 ,元素个数,元素占用的空间(sizeof),比较函数)
比较函数是一个自己写的函数 遵循 int com(const void *a,const void *b) 的格式。

#include "stdafx.h"
#include <algorithm>
#include "iostream"
#include "string"
using namespace std;
 
int compare(const void *a,const void *b)
{
    return *(int*)b-*(int*)a;  
}
 
 
int main(int argc, char* argv[])
{
    int a[11]={2,4,5,6,1,2,334,67,8,9,0},i;
    for(i=0;i<11;i++)
        cout<<a[i]<<',';
    qsort((void *)a,11,sizeof(int),compare);
    cout<<'\n';
    for(i=0;i<11;i++)
        cout<<a[i]<<',';
    return 0;
 
}

344.反转字符串

力扣题目链接

reverse库函数

reverse(s.begin(),s.end()) ; 

对于字符串,我们定义两个指针(也可以说是索引下标),一个从字符串前面,一个从字符串后面,两个指针同时向中间移动,并交换元素。

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

swap库函数可以有两种实现。

一种就是常见的交换数值:

int tmp = s[i];
s[i] = s[j];
s[j] = tmp;

一种就是通过位运算:

s[i] ^= s[j];
s[j] ^= s[i];
s[i] ^= s[j];

541. 反转字符串II

力扣题目链接

读题翻译写代码😶

class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s.begin() + i, s.begin() + i + k );
                continue;
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
            reverse(s.begin() + i, s.begin() + s.size());
        }
        return s;
    }
};

剑指Offer 05.替换空格

力扣题目链接

【法一】遍历是否是空格,新字符串拼接

class Solution {
public:
    string replaceSpace(string s) {
        string res;
        for(int i = 0; i < s.size(); i++){
        	if(s[i] == ' ') res += "%20";
        	else res += s[i];
        }
        return res;
    }
};
class Solution {
public:
    string replaceSpace(string s) {
        string res;
        for(auto& c : s) {
            if(c == ' ') res += "%20";
            else res.push_back(c);
        }
        return res;
    }
};

【法二】原地,数组扩容,统计空格个数,从后向前进行操作。

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

151.翻转字符串里的单词

力扣题目链接

c++没有split…,换思路:对于字符串不可变的语言(Java),首先得把字符串转化成其他可变的数据结构,同时还需要在转化的过程中去除空格, 对于字符串可变的语言(c++),就不需要再额外开辟空间了,直接在字符串上原地实现。在这种情况下,反转字符和去除空格可以一起完成

fig
  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转
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.左旋转字符串

力扣题目链接

【法一】原地,局部反转+整体反转 达到左旋转

  1. 反转区间为前n的子串
  2. 反转区间为n到末尾的子串
  3. 反转整个字符串
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()子串拼接,但开辟新空间

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

28. 实现 strStr()

力扣题目链接

class Solution {
public:
    int strStr(string haystack, string needle) {
        return haystack.find(needle);
    }
};

不用库函数,暴力匹配

class Solution {
public:
    int strStr(string s, string p) {
        int n = s.size(), m = p.size();
        for(int i = 0; i <= n - m; i++){
            int j = i, k = 0; 
            while(k < m and s[j] == p[k]){
                j++;
                k++;
            }
            if(k == m) return i;
        }
        return -1;
    }
};

优化⬇️

KMP字符串匹配

当出现字符串不匹配时,**因为找到了最长相等的前缀和后缀,匹配失败的位置是后缀子串的后面,那么我们找到与其相同的前缀的后面从新匹配,**避免从头再去做匹配了。

需要构建前缀表,用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。

从匹配串某个位置跳转下一个匹配位置这一过程是与原串无关的,我们将这一过程称为找 next 点。

显然我们可以预处理出 next 数组,数组中每个位置的值就是该下标应该跳转的目标位置( next 点)

next数组就可以是前缀表,但是很多实现都是把前缀表统一减一(右移一位,初始位置为-1)之后作为next数组。

class Solution {
public:
    int strStr(string s, string p) {
        int n = s.size(), m = p.size();
        if(m == 0) return 0;
        //设置哨兵
        s.insert(s.begin(),' ');
        p.insert(p.begin(),' ');
        vector<int> next(m + 1);
        //预处理next数组
        for(int i = 2, j = 0; i <= m; i++){
            while(j and p[i] != p[j + 1]) j = next[j];
            if(p[i] == p[j + 1]) j++;
            next[i] = j;
        }
        //匹配过程
        for(int i = 1, j = 0; i <= n; i++){
            while(j and s[i] != p[j + 1]) j = next[j];
            if(s[i] == p[j + 1]) j++;
            if(j == m) return i - m;
        }
        return -1;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值