标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”

简介: 标准库中的string类(中)+仅仅反转字母+字符串中的第一个唯一字符+字符串相加——“C++”“Leetcode每日一题”

各位CSDN的uu们好呀,今天,继续小雅兰西嘎嘎的学习,标准库中的string类,下面,让我们一起进入西嘎嘎string的世界吧!!!


string类的常用接口说明

Leetcode每日一题


string类的常用接口说明

标准库中的string类(上)——“C++”-CSDN博客

string类对象的容量操作

max_size

max_size:能开的最大的大小空间!

int main()
{
  string s1;
  string s2("hello world");
 
  cout << s1.max_size() << endl;
  cout << s2.max_size() << endl;
  return 0;
}

不同的编译器下 这个值都是不一样的!所以没有什么参考意义!

它说能开这么大的空间,但是实际上,真的能开这么大的空间吗?答案是不一定的!


reserve

reserve  保留

reverse 反转 逆置

抛异常了!

try
{
  string s1;
  string s2("hello world");
 
  // 实践中没有参考和使用的价值
  cout << s1.max_size() << endl;
  cout << s2.max_size() << endl;
  s1.reserve(s1.max_size());
}
catch (const exception& e)
{
  cout << e.what() << endl;
}

虽然这个东西没有什么价值,可是也不能把它删掉,现在的编译器只能向前兼容,因为怕之前有人用这个功能写了一个什么东西,本来用的好好的,结果编译器一更新,反而报错了!


capacity

try
{
  string s1;
  string s2("hello world");
 
  cout << s1.capacity() << endl;
  cout << s2.capacity() << endl;
}
catch (const exception& e)
{
  cout << e.what() << endl;
}

容量都是15!

但是实际上,空间不是开了15,而是开了16!

为什么开了16呢?因为在这个地方,容量不是指的开的多大的空间,它是指的我到底能存多少有效字符!为什么这两个东西不同呢?空间是多大和到底能存储多少个有效字符为什么不是等价的呢?因为有‘\0’,在结尾处标识的这个‘\0’不算有效字符!

现在capacity是15,代表我能存储15个有效字符,但是我空间必须得多开一个,用来放'\0'!

现在,这个capacity的值就变了!实际上开的空间大小是48!!!

下面,我们可以来检测一下string的扩容机制:

int main()
{
   try
   {
       string s1;
       string s2("hello worldxxxxxxxxxxxxx");
     

       size_t old = s1.capacity();
       cout << old << endl;

       //检测string的扩容机制
       for (size_t i = 0; i < 500; i++)
       {
           s1.push_back('x');
           if (old != s1.capacity())
           {
               cout << s1.capacity() << endl;
               old = s1.capacity();
           }
       }
   }
   catch (const exception& e)
   {
       cout << e.what() << endl;
   }

   return 0;
}

不同的编译器下,扩容机制也是不一样的!

但是扩容的代价是非常大的,所以:确定需要多少空间,提前开好空间即可!

s1.reserve(500);

但是不一定是开500,也有可能比500大!!!

但是开了511!

研究这个其实也是没有意义的!因为不同的编译器下所做的不同,VS下就进行了对齐!!!

所以有的时候,尤其是一些和空间、容量相关的代码,在不同的平台下,所运行的结果不同,这是可以理解的!!!

函数名称 功能说明
size(重点) 返回字符串有效字符长度
length 返回字符串有效字符长度
capacity 返回空间总大小
empty(重点) 检测字符串释放为空串,是返回true,否则返回false
clear(重点) 清空有效字符
reserve(重点) 为字符串预留空间
resize(重点) 将有效字符的个数该成n个,多出的空间用字符c填充

注意:

  • size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。
  • clear()只是将string中有效字符清空,不改变底层空间大小。
  • resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。  
  • reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小。

resize

reserve是用来扩容的,那么它会不会缩容呢?

int main()
{
  string s1;
  cout << s1.capacity() << endl;
  s1.reserve(10);
  cout << s1.capacity() << endl;
 
 
  string s2("hello worldxxxxxxxxxxxxx");
  cout << s2.capacity() << endl;
  s2.reserve(10);
  cout << s2.capacity() << endl;
  
  return 0;
}

不管是有数据还是没数据,reserve都是不会缩容的!可以理解为reserve仅仅是用来扩容的!

缩容的代价比较大!!!

但是:在不同的编译器下,所产生的结果仍然是不一样的!在Linux下,就会缩容;在VS下,就不会缩容!

在Linux下,确实会缩容,但是这个缩容又比较“怪异”,如果是有数据的情况下,缩容后的容量比现有的size还要小,不会删除数据,最小缩到size!!!

综上所述:reserve只影响空间(容量),不影响数据!

可是:resize就不一样了!它既影响容量,也影响数据!

int main()
{
  string s1("hello world");
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  s1.resize(100);
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  return 0;
}

int main()
{
  string s1("hello world");
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  //> capacity
  //s1.resize(100);
  s1.resize(100, 'x');
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
 
  // size < n < capacity
  string s2("hello world");
  cout << s2.size() << endl;
  cout << s2.capacity() << endl;
  cout << s2 << endl;
  s2.resize(12);
  cout << s2.size() << endl;
  cout << s2.capacity() << endl;
  cout << s2 << endl;
  return 0;
}

当size < n < capacity时,在VS下,也不会缩容!

int main()
{
  string s1("hello world");
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
  //> capacity    扩容+尾插
  //s1.resize(100);
  s1.resize(100, 'x');
  cout << s1.size() << endl;
  cout << s1.capacity() << endl;
  cout << s1 << endl;
 
  // size < n < capacity    尾插
  string s2("hello world");
  cout << s2.size() << endl;
  cout << s2.capacity() << endl;
  cout << s2 << endl;
  s2.resize(12);
  cout << s2.size() << endl;
  cout << s2.capacity() << endl;
  cout << s2 << endl;
 
  // n < size   删除数据,保留前n个
  string s3("hello world");
  cout << s3.size() << endl;
  cout << s3.capacity() << endl;
  cout << s3 << endl;
  s3.resize(10);
  cout << s3.size() << endl;
  cout << s3.capacity() << endl;
  cout << s3 << endl;
  return 0;
}

那在Linux下,又是不是这样呢?

我们会发现,在Linux下,resize也是不会缩容的!!!


at

int main()
{
   try
   {
       string s1("hello world");
       cout << s1[11] << endl;
       cout << s1[20] << endl;
   }
   catch (const exception& e)
   {
       cout << e.what() << endl;
   }
   return 0;
}

在C语言中,数组越界访问是不会报错的,可是在西嘎嘎中,就报错了!

at和[]的功能是一样的!!!

int main()
{
   try
   {
       string s1("hello world");
       cout << s1[11] << endl;
       //cout << s1[20] << endl;
       cout << s1.at(20) << endl;
   }
   catch (const exception& e)
   {
       cout << e.what() << endl;
   }
   return 0;
}

一个是偏暴力一点的检查,一个是偏温柔一点的检查!

一般是用[]比较多,也就是偏暴力一点!

剩下的back和front是C++11的新特性!


string类对象的修改操作

  • 增 +=(push_back/append)/insert
  • 删 erase
  • 查 []
  • 改 []/at/迭代器
函数名称 功能说明
push_back 在字符串后尾插字符c
append 在字符串后追加一个字符串
opeator+= 在字符串后追加字符串str
C str 返回C格式字符串
find+npos 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr 在str中从pos位置开始,截取n个字符,然后将其返回

注意:

  • 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
  • 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

append

int main()
{
  string s1("hello");
  s1.push_back(' ');
  s1.append("world");
  cout << s1 << endl;
 
  return 0;
}

int main()
{
  string s1("hello");
  s1.push_back(' ');
  s1.append("world");
  cout << s1 << endl;
 
  string s2 = "xxxx";
  const string& s3 = "xxxxxxxx";
  
  s2.append(++s1.begin(), --s1.end());
  cout << s2 << endl;
  return 0;
}

但是,不管是push_back还是append,都不是西嘎嘎最喜欢用的,西嘎嘎最喜欢用的是这个:


operator+=

int main()
{
   string s1("hello");
   s1.push_back(' ');
   s1.append("world");
   cout << s1 << endl;

   string s2 = "xxxx";
   const string& s3 = "xxxxxxxx";
   
   s2.append(++s1.begin(), --s1.end());
   cout << s2 << endl;

   s1 += '!';
   s1 += "xxxxx";
   s1 += s2;
   cout << s1 << endl;
   return 0;
}

 


Leetcode每日一题

仅仅反转字母

力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

class Solution {
 
public:
    bool isLetter(char ch)
    {
        if(ch >= 'a' && ch <= 'z')
            return true;
        if(ch >= 'A' && ch <= 'Z')
            return true;
        return false;
    }
    string reverseOnlyLetters(string S) {
    if(S.empty())
        return S;
    
    size_t begin = 0, end = S.size()-1;
    while(begin < end)
    {
        while(begin < end && !isLetter(S[begin]))
            ++begin;
    
        while(begin < end && !isLetter(S[end]))
            --end;
    
        swap(S[begin], S[end]);
        ++begin;
        --end;
    }
    
    return S;
    }
 
};

字符串中的第一个唯一字符

class Solution {
public:
    int firstUniqChar(string s) {
            int count[26]={0};
            for(auto ch:s)
            {
                count[ch-'a']++;
                //'a'的ASCII码值是97
                //计数
            }
            //找第一个只出现一次的
            //再次遍历,拿到每个字符
            for(size_t i=0;i<s.size();i++)
            {
                if(count[s[i]-'a']==1)
                {
                    return i;
                }
            }
            return -1;
    }
};

另一种写法:

class Solution {
 
public:
     int firstUniqChar(string s) {
 
         // 统计每个字符出现的次数
 
         int count[256] = {0};
         int size = s.size();
         for(int i = 0; i < size; ++i)
             count[s[i]] += 1;
     
         // 按照字符次序从前往后找只出现一次的字符
 
         for(int i = 0; i < size; ++i)
             if(1 == count[s[i]])
                 return i;
 
             return -1;
        }
};

字符串相加

“大数运算”

class Solution {
public:
    string addStrings(string num1, string num2) {
            int end1=num1.size()-1;
            int end2=num2.size()-1;
            //进位
            int next=0;
            string retStr;
            while(end1>=0||end2>=0)
            {
                int value1=0;
                int value2=0;
                if(end1>=0)
                {
                    value1=num1[end1--]-'0';
                }
                if(end2>=0)
                {
                    value2=num2[end2--]-'0';
                }
                int ret=value1+value2+next;
                next=ret/10;
                ret=ret%10;
                retStr+=('0'+ret);
            }
            if(next==1)
            {
                retStr+='1';
            }
            reverse(retStr.begin(),retStr.end());
            return retStr;
    }
};


好啦,小雅兰今天的string类的使用以及Leetcode每日一题的内容就到这里啦,下篇博客继续string类的使用!!!加油!!!奥利给!!!


相关文章
|
2月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
4天前
|
编译器 C++ 容器
【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
C++11为C++带来了革命性变化,引入了列表初始化、右值引用、移动语义、类的新默认成员函数和lambda表达式等特性。列表初始化统一了对象初始化方式,initializer_list简化了容器多元素初始化;右值引用和移动语义优化了资源管理,减少拷贝开销;类新增移动构造和移动赋值函数提升性能;lambda表达式提供匿名函数对象,增强代码简洁性和灵活性。这些特性共同推动了现代C++编程的发展,提升了开发效率与程序性能。
33 12
|
13天前
|
Go 索引
【LeetCode 热题100】394:字符串解码(详细解析)(Go语言版)
本文详细解析了 LeetCode 热题 394:字符串解码。题目要求对编码字符串如 `k[encoded_string]` 进行解码,其中 `encoded_string` 需重复 `k` 次。文章提供了两种解法:使用栈模拟和递归 DFS,并附有 Go 语言实现代码。栈解法通过数字栈与字符串栈记录状态,适合迭代;递归解法则利用函数调用处理嵌套结构,代码更简洁。两者时间复杂度均为 O(n),但递归需注意栈深度问题。文章还总结了解题注意事项及适用场景,帮助读者更好地掌握字符串嵌套解析技巧。
32 6
|
1月前
|
数据处理
鸿蒙开发:ArkTs字符串string
字符串类型是开发中非常重要的一个数据类型,除了上述的方法概述之外,还有String对象,正则等其他的用处,我们放到以后得篇章中讲述。
94 19
|
27天前
|
存储 机器学习/深度学习 缓存
🚀 力扣热题 394:字符串解码(详细解析)(Go语言版)
文章提供了两种解法:栈结构和递归解法。栈解法通过维护数字栈与字符串栈,依次处理 `[` 和 `]`,构造解码结果;递归解法则利用函数调用逐层解析嵌套结构。两者时间复杂度均为 $O(n)$,空间复杂度也为 $O(n)$。栈解法直观易懂,适合初学者;递归解法优雅简洁,适合处理深度嵌套规则。掌握这两种方法,可灵活应对类似问题,提升解题能力。
63 11
|
1月前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
48 16
|
1月前
|
编译器 C++
类和对象(中 )C++
本文详细讲解了C++中的默认成员函数,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载和取地址运算符重载等内容。重点分析了各函数的特点、使用场景及相互关系,如构造函数的主要任务是初始化对象,而非创建空间;析构函数用于清理资源;拷贝构造与赋值运算符的区别在于前者用于创建新对象,后者用于已存在的对象赋值。同时,文章还探讨了运算符重载的规则及其应用场景,并通过实例加深理解。最后强调,若类中存在资源管理,需显式定义拷贝构造和赋值运算符以避免浅拷贝问题。
|
1月前
|
存储 编译器 C++
类和对象(上)(C++)
本篇内容主要讲解了C++中类的相关知识,包括类的定义、实例化及this指针的作用。详细说明了类的定义格式、成员函数默认为inline、访问限定符(public、protected、private)的使用规则,以及class与struct的区别。同时分析了类实例化的概念,对象大小的计算规则和内存对齐原则。最后介绍了this指针的工作机制,解释了成员函数如何通过隐含的this指针区分不同对象的数据。这些知识点帮助我们更好地理解C++中类的封装性和对象的实现原理。
|
1月前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
110 6
|
1月前
|
编译器 C++
类和对象(下)C++
本内容主要讲解C++中的初始化列表、类型转换、静态成员、友元、内部类、匿名对象及对象拷贝时的编译器优化。初始化列表用于成员变量定义初始化,尤其对引用、const及无默认构造函数的类类型变量至关重要。类型转换中,`explicit`可禁用隐式转换。静态成员属类而非对象,受访问限定符约束。内部类是独立类,可增强封装性。匿名对象生命周期短,常用于临时场景。编译器会优化对象拷贝以提高效率。最后,鼓励大家通过重复练习提升技能!

热门文章

最新文章