数据结构—字符串

文章目录

  • 7.字符串
    • (1).字符串及其ADT
      • #1.基本概念
      • #2.ADT
    • (2).字符串的基本操作
      • #1.求子串substr
      • #2.插入字符串insert
      • #3.其他操作
    • (3).字符串的模式匹配
      • #1.简单匹配(Brute-Force方法)
      • #2.KMP算法
        • I.kmp_match()
        • II.getNext()
      • #3.还有更多
    • 小结
    • 附录:我自己写的string

7.字符串

  字符串几乎算得上是我们平时最常用的数据结构了,比如你看到的这篇博客,它也是由一系列的字符组成的。说实话,字符串相关的问题其实多的非常离谱,并且往后的各种问题也非常复杂。

  我们可以举这么一个例子,假设我给你一段C语言的代码:

#include <stdio.h>
int main()
{int a = 0;scanf("%d", &a);printf("%d\n", a + 10);return 0;
}

  这一段代码在你眼里看来是代码,但是在gcc看来,就是一串可能包含代码的字符串,经过处理之后,我们需要把这段代码解析成下面正确的汇编代码,从而经过之后的链接器等完成最终可执行文件的生成操作,而读入代码,生成汇编文件这个过程就涉及到了有限自动机(FAM) 这个模型,它将一个系统分成若干可数个离散的状态,在识别到特定模式后,进行对应的跳转,最终再进行判断。

        .file   "a.c".text.section        .rodata.str1.1,"aMS",@progbits,1
.LC0:.string "%d"
.LC1:.string "%d\n".text.globl  main.type   main, @function
main:
.LFB11:.cfi_startprocsubq    $24, %rsp.cfi_def_cfa_offset 32movl    $0, 12(%rsp)leaq    12(%rsp), %rsimovl    $.LC0, %edimovl    $0, %eaxcall    __isoc99_scanfmovl    12(%rsp), %eaxleal    10(%rax), %esimovl    $.LC1, %edimovl    $0, %eaxcall    printfmovl    $0, %eaxaddq    $24, %rsp.cfi_def_cfa_offset 8ret.cfi_endproc
.LFE11:.size   main, .-main.ident  "GCC: (GNU) 13.2.0".section        .note.GNU-stack,"",@progbits

  而字符串的操作还远不止这些,关于有限自动机以及编译器的实现细节等内容,你将会在未来的编译原理课程中学到(当然是你们学校开的课,我还没有写编译原理博客的计划),在这一篇当中,我们会介绍字符串的ADT,基本操作,以及两种常用的字符串模式匹配算法

(1).字符串及其ADT

#1.基本概念

  假设V是程序语言所使用的字符集,由字符集V中的字符所组成的任何有限序列,称为字符串

不包含任何字符的字符串称为空字符串,而字符串的长度即为一个字符串所包含的字符个数;某个串的子串则是这个串中任意一个连续的子序列

  所以字符串也是一种线性表,其中的每个元素都是一个字符,这样我们用数组和链表理论上讲都是可以实现字符串的,不过通常情况下,我们会采取顺序存储的方式来实现字符串,因为有些算法可能涉及到随机访问,所以用顺序存储会方便些

#2.ADT

  具体的我就不提了,这里就说一说关于字符串的一些操作:

  • 创建、赋值、拷贝、清空、销毁
  • 串互相比较
  • 求串的长度
  • 连接两个字符串
  • 取出字符串的子串
  • 判断是否为空串
  • 子串替换、插入、删除

  接下来我们就来看看这些基本操作的实现方式。

(2).字符串的基本操作

  这里的字符串的容器采取STL vector实现,具体你可以试着自己写一写,我在GitHub上开源了一个MySTL,其中有基于vector<T>的basic_string<T>实现:MySTL-Voltline

#1.求子串substr

  求子串其实还挺简单的,只是有一些边界情况需要考虑,比如beg < end,beg、end > 0,end < _size这样,所以就可以得到以下的代码:

string substr(int beg, int end) const
{if (beg < 0) beg = 0;if (end > _size) end = _size;vector<char> vc;for (int i = beg; i <= end; i++) {vc.push_back(container[i]);}return string{ vc };
}

  还有一种子串的取法则是从位置i开始取j个字符形成子串,这个实现方法其实没什么太大的区别,你可以自己尝试实现。

#2.插入字符串insert

  这个函数其实和线性表向其中某个位置插入指定个数的元素的操作差不多,所以我们也可以很快地写出下面的代码:

string insert(const string& s1, int pos)
{if (pos > _size) pos = _size;else if (pos < 0) pos = 0;container.resize(_size + s1.size() + 100);// 给容器直接扩容,避免之后再挪动的时候出现越界访问的问题int mov = s1.size();for (int i = _size-1; i >= pos; i--) {container[i + mov] = container[i]; }for (int i = pos; i < mov; i++) {container[i] = s1.container[i - pos];}return *this;
}

#3.其他操作

  其他操作真的很简单,比如concat,如果操作的是vector,一个个push_back就好了,这些操作还是自己实现一下吧。

(3).字符串的模式匹配

  什么是模式匹配呢,听起来有点复杂,不过其实很简单,就是依照查找子串的事情,比如:在hellowbabababbabbba中找到babb

#1.简单匹配(Brute-Force方法)

怎么找呢?很自然的想法就是一个个去遍历,比如这样:

int find_bf(const string& t, const string& p, int pos)
{int i{ pos }, j{ 0 };int n = t.size(), m = p.size();while (i < n && j < m) {if (t[i] == p[i]) { i++, j++;}else {i += 1-j;j = 0;}}if (j == m) return i - m;else return -1;
}

  很简单,每到一个字符就往后匹配一次,匹配不上就继续遍历,如果匹配上了就直接返回,这样就好了

  假设正文字符串长度为n模式字符串长度为m,那么这个匹配方式的时间复杂度是 O ( n m ) O(nm) O(nm),如果模式串和正文串长度差不多,这个匹配方式的时间复杂度就会退化到 O ( n 2 ) O(n^2) O(n2)的时间复杂度,这有点太慢了。

#2.KMP算法

  Knuth,Morris和Pratt共同提出了一种全新的模式匹配算法,这个算法能够在 O ( n + m ) O(n+m) O(n+m)的线性时间复杂度内完成字符串的模式匹配。

  我们先来思考一下,Brute-Force方法到底有什么问题,比如t = abababcbababba,p = ababc,BF方法是这样:
p12

  我们会发现,p和t在第一次匹配的时候,前面的abab都已经匹配上了,只有c和a发生了失配,而因为失配,第二次我们把整个字符串往后挪动一次匹配,还得从a开始,这样我们貌似忘记了前面四个字符是匹配的了

  如果我们试着看看t可能可以发现这么一件事,出现的第二个ab后面又是a,而在字符串p中,第一个ab后出现的也是a,所以如果我们的p可以这么跳转:
p13

  只让p对应的那个指针动,动到刚才我们发现的"aba"的地方,就可以去掉第二次匹配,这个例子举的可能差距不大,我们把t变成abababababcbab,那么情况就会变成这样:
p14

  比较次数明显减少!所以KMP算法的核心就在于:如何找到模式字符串本身具备的特性,通过它本身的性质得到一个失配跳转值,从而尽可能增加每一次模式字符串跳转的字符数,从而大大减少重复匹配的次数,接下来我们来具体提一提怎么实现KMP算法

I.kmp_match()

  这次我打算先介绍一下,我们如何通过这个想法进行匹配,假设我们已经获得了对于模式字符串的next数组(这个等会儿再来看怎么实现),接下来要对正文串t和模式串p进行匹配,那么基于前面我们说的想法:在匹配的时候就继续前进,失配的时候根据失配跳转值让p向前跳,如果到第一个字符还是失配,那就不可能在现在已经走过的字符里找到能匹配的了,这时候只要让t的指针往后跳一个就行了,所以我们可以得出下面的代码:

struct chars
{char c;  // 字符int idx; // 跳转值
};int kmp(const string& t, const string& p)
{int ans{ -1 };vector<chars> next = getNext(p);int t_size = t.size(), p_size = p.size();int i{ 0 }, j{ 0 };while (i < t_size) {if (j == -1 || t[i] == p[j]) {i++, j++;}else {if (j < p_size) j = next[j].idx;}if (j == p_size) {ans = i - j;break;}}return ans;
}

  我们首先用getNext()函数获得next数组,然后进行基于next数组的匹配,next数组的数据模式如下:第一个字符对应的值为-1,其他的字符对应的值则是对应字符失配时的跳转值,如果找到的next值是-1,就让指向正文串的"指针"i向后移动一位,否则就让指向模式串的"指针"j移动到next[j]的位置上去。

  那么接下来,我们就来看看这个getNext()函数怎么写的吧!

II.getNext()

  首先说一说前缀后缀的概念,前缀就是自第一个字符起的所有连续子串,后缀就是自最后一个字符起的所有连续子串,提到前缀和后缀主要是为了引入next的计算原理。

  接下来我们来看看next数组是怎么生成的,next数组的跳转顺序基于这样一个规则:当失配发生时,失配字符a的上一个字符b对应的某一个子串S在不包含当前子串的最长前缀中,子串S最后一次出现的位置中,字符b的位置,有点拗口,我们来详细地解释一下,例如:p = abcabd,我们找next的规则就是基于每个下一个字符都有可能是失配字符来做的,那么我们可以得到下面的一张表:
p15

  a首先是-1没问题,b去往前找,只有a,那就赋为0吧。之后是c,往前找一个字符是b,它对应的idx是0,next[0]是a,a和b并不匹配,那就继续往前走,字符a对应的idx是-1,这时候就结束了,我们让c的idx为a的idx+1,也就是0。

  然后又是b,b前面一个字符是a,而a在前面的字符中出现过,在第0个,因此当这个b失配的时候,下一次我们可以试着从它前面一个字符出现的上一次的后面一个字符继续匹配,很显然,a在0出现了,后面一位是1,所以b就赋为1了。

  最后是d,d前面一个字符是b,而b上一次出现在数组中是1,那么d失配的时候,b满足,就跳转到上一次出现的b的后面一位,也就是c,对应的是2,所以d就赋为2了。

  然后我们就走完了全程,你发现了,虽然我们是在做子串和前缀的重叠匹配,但是实际上,因为每个字符的上一个字符总是已经包含了之前字符的重叠情况,因此我们只需要考虑当前这个字符是不是匹配就好了,这其实也能算是一种动态规划的思想,因为我们让每一次字符的查找都变得有用了起来,每一次下一个字符的查找都基于上一个查找的结果来确定,因此查找的过程就变得简单了起来:

  • 1.到了某一个字符c,先看看这个字符的前一个字符
  • 2.如果这个字符的前一个字符b和b自己的next值对应的字符相同,那就让c的next值等于b的next值+1(允许你从上一次b出现的后面一个字符开始匹配)
  • 3.如果这个字符的前一个字符b和b自己的next值对应的字符不相同,那就让继续找到b的next值对应的字符对应的next值,继续往前找,直到找到一个匹配的字符,或者找到第一个字符(对应的next值为-1),如果找到匹配的字符,则类似2操作,让c的next值等于找到的这个字符的位置+1;否则就赋值为0,对应最前的字符

  很好,那代码就很好写了:

vector<chars> getNext(const string& sub_str)
{vector<chars> next;size_t sub_str_size{ sub_str.size() };next.push_back(chars{ sub_str[0], -1 });for (size_t i = 1; i < sub_str_size; i++) {next.push_back(chars{ sub_str[i], 0 });}for (size_t i = 1; i < sub_str_size; i++) {if (next[i - 1].idx != -1 && next[i - 1].c == next[next[i - 1].idx].c) {next[i].idx = next[i - 1].idx + 1;}else {int j = next[i - 1].idx;while (j != -1 && next[j].c != next[i - 1].c) {j = next[j].idx;}next[i].idx = j + 1;}}return next;
}

  我这里next存的是一个叫做chars的结构体,它对应的结构是这样:

struct chars
{char c;  // 字符int idx; // 跳转值
};

  其实你只存这个idx也是可以的,你可以稍微改改,有一个特别需要注意的点:

    for (size_t i = 1; i < sub_str_size; i++) {next.push_back(chars{ sub_str[i], 0 });}

  这个数组在初始化的时候,我们把所有后续字符的idx值都赋为0,如果没有后续的操作,这个情况如果调用kmp_match函数的匹配过程就会和前面说的Brute-Force方式完全一样了,所以后续的找next过程还是非常重要的呢!那么KMP算法其实到这里基本上就结束了,我们会发现,如果真的理解了它的做法,其实感觉还挺简单的,实际上就是一个动态规划的思想,很多动态规划的问题也是这样,其实想出来之后它的思路并不复杂,但是要想到这个思路的过程是相当困难的。

#3.还有更多

  其实字符串的匹配方式还有很多很多,例如BM算法等,因为时间的限制,在这里我就不做过多介绍了。

小结

  字符串这一篇,相当的短啊哈哈哈哈哈哈,毕竟这一部分在数据结构中我们主要还是以掌握KMP算法为主,毕竟它在模式匹配上表现出的时间复杂度 O ( n + m ) O(n+m) O(n+m)是非常优秀的。下一篇我们将会介绍一系列内部排序的算法。
  然后,我会在下面给出我写过的MySTL中的basic_string的代码,仅供参考,如果你发现了什么bug,一定要告诉我哦!这对我帮助很大!

附录:我自己写的string

#pragma once
#include <iostream>
#include <cstring>
#include "vector.h"namespace MySTL 
{template <typename T>class basic_string{private:vector<T> container;size_t _size;public:using iterator = T*;using const_iterator = const T*;inline static size_t npos = -1;public:basic_string();basic_string(const char* _c_str);basic_string(const char* _c_str, size_t _size);basic_string(const char* _c_str, size_t _begin, size_t _size);basic_string(size_t _size, char c);basic_string(const basic_string<T>& _str);basic_string(basic_string<T>&& _mv_str) noexcept;basic_string(std::initializer_list<T> l);~basic_string();basic_string<T>& operator=(const basic_string<T>& _Right);basic_string<T>& operator=(basic_string<T>&& _Right) noexcept;basic_string<T>& operator=(const char* _str);basic_string<T>& operator=(char c);basic_string<T> operator+(const basic_string<T>& _Right);basic_string<T> operator+(const char* _str);basic_string<T> operator+(char c);basic_string<T>& operator+=(const basic_string<T>& _Right);basic_string<T>& operator+=(const char* _str);basic_string<T>& operator+=(char c);T& operator[](size_t _index);const T& operator[](size_t _index) const;T& at(size_t _index);bool operator==(const basic_string<T>& _Right);bool operator!=(const basic_string<T>& _Right);bool operator>(const basic_string<T>& _Right);bool operator<(const basic_string<T>& _Right);bool operator>=(const basic_string<T>& _Right);bool operator<=(const basic_string<T>& _Right);bool operator==(const char* _c_Right);bool operator!=(const char* _c_Right);bool operator>(const char* _c_Right);bool operator<(const char* _c_Right);bool operator>=(const char* _c_Right);bool operator<=(const char* _c_Right);size_t size() const noexcept;size_t capacity() const noexcept;bool empty() const noexcept;const T* data();const T* c_str();iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;void reserve(size_t new_capacity_size);void resize(size_t new_elem_size);void clear();void erase(const size_t _Where);void erase(const size_t _Off, const size_t _Count);void erase(iterator _begin, iterator _end);void erase(iterator _pos);void append(size_t _Count, char c);void append(const basic_string<T>& _str);void append(const basic_string<T>& _str, size_t _Begin = 0);void append(const basic_string<T>& _str, size_t _Begin, size_t _Count);void append(const char* _str);void append(const char* _str, size_t _Begin);void append(const char* _str, size_t _Begin, size_t _Count);void append(std::initializer_list<char> l);void push_back(char c);size_t find(char c, size_t _begin_pos = 0);size_t find(const char* _str, size_t _begin_pos = 0);size_t find(const basic_string<T>& _str, size_t _begin_pos = 0);void swap(basic_string<T>& _Right);template<typename U>friend std::ostream& operator<<(std::ostream& output, const basic_string<U>& _str){for (auto& i : _str) {output << i;}return output;}template<typename U>friend std::istream& operator>>(std::istream& input, basic_string<U>& _str){_str.clear();U c = input.get();while (c != ' ' && c != '\n' && c != '\t') {_str.push_back(c);c = input.get();}return input;}};template<typename T>basic_string<T>::basic_string(){container = vector<unsigned char>{};}template<typename T>basic_string<T>::basic_string(const char* _c_str){size_t length{ strlen(_c_str) };_size = length;container = vector<T>();for (size_t i = 0; i < length; i++) {container.push_back(*(_c_str + i));}container.push_back('\0');}template<typename T>basic_string<T>::basic_string(const char* _c_str, size_t _size){size_t length{ _size };if (_size > strlen(_c_str)) {length = strlen(_c_str);}_size = length;container = vector<T>();for (size_t i = 0; i < length; i++) {container.push_back(*(_c_str + i));}container.push_back('\0');}template<typename T>basic_string<T>::basic_string(const char* _c_str, size_t _begin, size_t _size){size_t c_str_len{ strlen(_c_str) };container = vector<T>();if (_begin > c_str_len) {_size = 0;}else {size_t length{ _size };if (_size > strlen(_c_str + _begin)) {length = strlen(_c_str + _begin);}_size = length;for (size_t i = _begin; i < length; i++) {container.push_back(*(_c_str + i));}container.push_back('\0');}}template<typename T>basic_string<T>::basic_string(size_t _size, char c){container = vector<T>(_size, c);_size = _size;}template<typename T>basic_string<T>::basic_string(const basic_string<T>& _str){container = vector<T>(_str.container);_size = _str._size;}template<typename T>basic_string<T>::basic_string(basic_string<T>&& _mv_str) noexcept{container = std::move(_mv_str.container);_size = _mv_str._size;_mv_str.container = vector<T>{};}template<typename T>basic_string<T>::basic_string(std::initializer_list<T> l){container = vector<T>(l.size() + 128);_size = l.size();for (auto it = l.begin(); it != l.end(); it++) {container.push_back(static_cast<T>(*it));}container.push_back('\0');}template<typename T>basic_string<T>::~basic_string(){_size = 0;container.~vector();}template<typename T>basic_string<T>& basic_string<T>::operator=(const basic_string<T>& _Right){container.~vector();container = _Right.container;_size = _Right._size;return *this;}template<typename T>basic_string<T>& basic_string<T>::operator=(basic_string<T>&& _Right) noexcept{container.~vector();container = std::move(_Right.container);_size = _Right._size;return *this;}template<typename T>basic_string<T>& basic_string<T>::operator=(const char* _str){container.~vector();size_t length{ strlen(_str) };container = vector<T>(length + 128);_size = 0;for (size_t i = 0; i < length; i++) {container.push_back(_str[i]);_size++;}return *this;}template<typename T>basic_string<T>& basic_string<T>::operator=(char c){clear();push_back(c);return *this;}template<typename T>basic_string<T> basic_string<T>::operator+(const basic_string<T>& _Right){basic_string<T> temp{ *this };for (auto& i : _Right) {temp.push_back(i);}return temp;}template<typename T>basic_string<T> basic_string<T>::operator+(const char* _str){basic_string<T> temp{ *this };size_t length{ strlen(_str) };for (size_t i = 0; i < length; i++) {temp.push_back(_str[i]);}return temp;}template<typename T>basic_string<T> basic_string<T>::operator+(char c){basic_string<T> temp{ *this };temp.push_back(c);return temp;}template<typename T>basic_string<T>& basic_string<T>::operator+=(const basic_string<T>& _Right){for (auto& i : _Right) {push_back(i);}return *this;}template<typename T>basic_string<T>& basic_string<T>::operator+=(const char* _str){size_t length{ strlen(_str) };for (size_t i = 0; i < length; i++) {push_back(_str[i]);}return *this;}template<typename T>basic_string<T>& basic_string<T>::operator+=(char c){push_back(c);return *this;}template<typename T>T& basic_string<T>::operator[](size_t _index){return container[_index];}template<typename T>const T& basic_string<T>::operator[](size_t _index) const{return container[_index];}template<typename T>T& basic_string<T>::at(size_t _index){if (_index <= _size) {return container[_index];}else {throw std::out_of_range("Index Out of Range");}}template<typename T>bool basic_string<T>::operator==(const basic_string<T>& _Right){if (_size != _Right._size) {return false;}else {for (size_t i = 0; i < _size; i++) {if (container[i] != _Right[i]) return false;}return true;}}template<typename T>bool basic_string<T>::operator!=(const basic_string<T>& _Right){return !(*this == _Right);}template<typename T>bool basic_string<T>::operator>(const basic_string<T>& _Right){size_t min_size{ _size < _Right.size() ? _size : _Right.size() };for (size_t i = 0; i < min_size; i++) {if (container[i] > _Right[i]) return true;else if (container[i] < _Right[i]) {return false;}}return _size > _Right.size();}template<typename T>bool basic_string<T>::operator<(const basic_string<T>& _Right){return (*this <= _Right) && (*this != _Right);}template<typename T>bool basic_string<T>::operator>=(const basic_string<T>& _Right){return (*this > _Right) || (*this == _Right);}template<typename T>bool basic_string<T>::operator<=(const basic_string<T>& _Right){return !(*this > _Right);}template<typename T>bool basic_string<T>::operator==(const char* _c_Right){if (strlen(_c_Right) != _size) return false;else {for (size_t i = 0; i < _size; i++) {if (container[i] != _c_Right[i])return false;}return true;}}template<typename T>bool basic_string<T>::operator!=(const char* _c_Right){return !(*this == _c_Right);}template<typename T>bool basic_string<T>::operator>(const char* _c_Right){size_t length{ strlen(_c_Right) };size_t min_size{ _size < length ? _size : length };for (size_t i = 0; i < min_size; i++) {if (container[i] > _c_Right[i]) return true;else if (container[i] < _c_Right[i]) {return false;}}return _size > length;}template<typename T>bool basic_string<T>::operator<(const char* _c_Right){return (*this <= _c_Right) && (*this != _c_Right);}template<typename T>bool basic_string<T>::operator>=(const char* _c_Right){return (*this > _c_Right) || (*this == _c_Right);}template<typename T>bool basic_string<T>::operator<=(const char* _c_Right){return !(*this > _c_Right);}template<typename T>size_t basic_string<T>::size() const noexcept{return _size;}template<typename T>size_t basic_string<T>::capacity() const noexcept{return container.capacity();}template<typename T>bool basic_string<T>::empty() const noexcept{if (_size != 0) return false;else return true;}template<typename T>const T* basic_string<T>::data(){return container.data();}template<typename T>const T* basic_string<T>::c_str(){return container.data();}template<typename T>typename basic_string<T>::iterator basic_string<T>::begin(){return container.begin();}template<typename T>typename basic_string<T>::iterator basic_string<T>::end(){return container.begin() + _size;}template<typename T>typename basic_string<T>::const_iterator basic_string<T>::begin() const{return container.begin();}template<typename T>typename basic_string<T>::const_iterator basic_string<T>::end() const{return container.begin() + _size;}template<typename T>void basic_string<T>::reserve(size_t new_capacity_size){container.reserve(new_capacity_size);}template<typename T>void basic_string<T>::resize(size_t new_elem_size){container.resize(new_elem_size);_size = new_elem_size;}template<typename T>void basic_string<T>::clear(){_size = 0;container.clear();}template<typename T>void basic_string<T>::erase(const size_t _Where){if (_Where <= _size) {_size = _Where;container.erase(container.begin() + _Where, container.end());container.push_back('\0');}}template<typename T>void basic_string<T>::erase(const size_t _Off, const size_t _Count){if (_Off <= _size) {if (_size - _Off > _Count) {_size -= _Count;container.erase(container.begin() + _Off, container.begin() + _Off + _Count);container[_size] = '\0';}else {erase(_Off);}}}template<typename T>void basic_string<T>::erase(basic_string<T>::iterator _begin, basic_string<T>::iterator _end){if (_end >= _begin) {if (_begin >= begin()) {size_t _Off = _begin - begin();size_t _Count = _end - _begin;if (_Off <= _size) {if (_size - _Off > _Count) {_size -= _Count;container.erase(container.begin() + _Off, container.begin() + _Off + _Count);container[_size] = '\0';}else {erase(_Off);}}}else {throw IteratorOutOfRangeException{};}}else {throw IteratorErrorException{};}}template<typename T>void basic_string<T>::erase(basic_string<T>::iterator _pos){if (_pos >= begin()) {if (_pos < end()) {size_t _Where = _pos - begin();if (_Where <= _size) {_size--;container.erase(_pos, _pos + 1);container[_size] = '\0';}}}else {throw IteratorErrorException{};}}template<typename T>void basic_string<T>::append(size_t _Count, char c){for (size_t i = 0; i < _Count; i++) {push_back(c);}}template<typename T>void basic_string<T>::append(const basic_string<T>& _str){*this += _str;}template<typename T>void basic_string<T>::append(const basic_string<T>& _str, size_t _Begin){if (_Begin <= _str.size()) {if (_Begin == 0) {*this += _str;}else {for (auto it = _str.begin() + _Begin; it != _str.end(); it++) {push_back(*it);}}}else {throw std::out_of_range("Begin index out of range!");}}template<typename T>void basic_string<T>::append(const basic_string<T>& _str, size_t _Begin, size_t _Count){if (_Begin <= _str.size()) {if (_Begin + _Count > _str.size()) {_Count = _str.size() - _Begin;}for (size_t i = 0; i < _Count; i++) {push_back(_str[_Begin + i]);}}else {throw std::out_of_range("Begin index out of range!");}}template<typename T>void basic_string<T>::append(const char* _str){*this += _str;}template<typename T>void basic_string<T>::append(const char* _str, size_t _Begin){if (_Begin <= strlen(_str)) {*this += (_str + _Begin);}else {throw std::out_of_range("Begin index out of range!");}}template<typename T>void basic_string<T>::append(const char* _str, size_t _Begin, size_t _Count){if (_Begin <= strlen(_str)) {if (strlen(_str) - _Begin < _Count) {_Count = strlen(_str) - _Begin;}if (_Count != 0) {for (size_t i = 0; i < _Count; i++) {push_back(_str[_Begin + i]);}}}else {throw std::out_of_range("Begin index out of range!");}}template<typename T>void basic_string<T>::append(std::initializer_list<char> l){for (auto& i : l) {push_back(i);}}template<typename T>void basic_string<T>::push_back(char c){if (_size == container.size()) {container.push_back(c);}else {container[_size] = c;}container.push_back('\0');_size++;}template<typename T>size_t basic_string<T>::find(char c, size_t _begin_pos){for (size_t i = _begin_pos; i < _size; i++) {if (container[i] == c) {return i;}}return npos;}template<typename T>size_t basic_string<T>::find(const char* _str, size_t _begin_pos){size_t length{ strlen(_str) };bool isFind{ true };if (_size < length) return npos;else {for (size_t i = _begin_pos; i < _size; i++) {if (_size - i >= length) {if (container[i] == _str[0]) {isFind = true;for (size_t j = 1; j < length; j++) {if (container[i + j] != _str[j]) {i = i + j - 1;isFind = false;break;}}if (isFind) return i;}}else {return npos;}}return npos;}}template<typename T>size_t basic_string<T>::find(const basic_string<T>& _str, size_t _begin_pos){size_t length{ _str.size() };bool isFind{ true };if (_size < length) return npos;else {for (size_t i = _begin_pos; i < _size; i++) {if (_size - i >= length) {if (container[i] == _str[0]) {isFind = true;for (size_t j = 1; j < length; j++) {if (container[i + j] != _str[j]) {i = i + j - 1;isFind = false;break;}}if (isFind) return i;}}else {return npos;}}return npos;}}template<typename T>void basic_string<T>::swap(basic_string<T>& _Right){basic_string<T> temp{ _Right };_Right.clear();for (auto& c : *this) {_Right.push_back(c);}clear();for (auto& c : temp) {push_back(c);}}template<typename T>bool operator==(const char* _Left, const basic_string<T>& _Right){return _Right == _Left;}template<typename T>bool operator!=(const char* _Left, const basic_string<T>& _Right){return _Right != _Left;}template<typename T>bool operator>(const char* _Left, const basic_string<T>& _Right){return _Right < _Left;}template<typename T>bool operator<(const char* _Left, const basic_string<T>& _Right){return _Right > _Left;}template<typename T>bool operator>=(const char* _Left, const basic_string<T>& _Right){return _Right <= _Left;}template<typename T>bool operator<=(const char* _Left, const basic_string<T>& _Right){return _Right >= _Left;}template<typename T>std::istream& getline(std::istream& input, basic_string<T>& _Target, const char _End = '\n'){_Target.clear();T c = input.get();while (c != '\n') {_Target.push_back(c);c = input.get();}return input;}using string = basic_string<char>;using wstring = basic_string<wchar_t>;
#ifdef __cpp_lib_char8_tusing u8string = basic_string<char8_t>;
#endif // __cpp_lib_char8_tusing u16string = basic_string<char16_t>;using u32string = basic_string<char32_t>;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/181517.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

幂等性(防重复提交)

文章目录 1. 实现原理2.使用示例3. Idempotent注解4. debug过程 主要用途&#xff1a;防止用户快速双击某个按钮&#xff0c;而前端没有禁用&#xff0c;导致发送两次重复请求。 1. 实现原理 幂等性要求参数相同的方法在一定时间内&#xff0c;只能执行一次。本质上是基于red…

【qemu逃逸】华为云2021-qemu_zzz

前言 虚拟机用户名&#xff1a;root 无密码 设备逆向 经过逆向分析&#xff0c;可得实例结构体大致结构如下&#xff1a; 其中 self 指向的是结构体本身&#xff0c;cpu_physical_memory_rw 就是这个函数的函数指针。arr 应该是 PCI 设备类结构体没啥用&#xff0c;就直接用…

5.3有效的括号(LC20-E)

算法&#xff1a; 题目中&#xff1a;左括号必须以正确的顺序闭合。意思是&#xff0c;最后出现的左括号&#xff08;对应着栈中的最后一个元素&#xff09;&#xff0c;应该先找到对应的闭合符号&#xff08;右括号&#xff09; 比如:s"( [ ) ]"就是False&#xf…

1.UML面向对象类图和关系

文章目录 4种静态结构图类图类的表示类与类之间的关系依赖关系(Dependency)关联关系(Association)聚合(Aggregation)组合(Composition)实现(Realization)继承/泛化(Inheritance/Generalization)常用的UML工具reference欢迎访问个人网络日志🌹🌹知行空间🌹🌹 4种静态结构…

spring boot导入导出excel,集成EasyExcel

一、安装依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</version></dependency>二、新建导出工具类 package com.example.springbootclickhouse.utils;import javax.s…

Zinx框架-游戏服务器开发003:架构搭建-需求分析及TCP通信方式的实现

文章目录 1 项目总体架构2 项目需求2.1 服务器职责2.2 消息的格式和定义 3 基于Tcp连接的通信方式3.1 通道层实现GameChannel类3.1.1 TcpChannel类3.1.2 Tcp工厂类3.1.3 创建主函数&#xff0c;添加Tcp的监听套接字3.1.4 代码测试 3.2 消息类的结构设计和实现3.2.1 消息的定义3…

基于EPICS stream模块的直流电源的IOC控制程序实例

本实例程序实现了对优利德UDP6720系列直流电源的网络控制和访问&#xff0c;先在此介绍这个项目中使用的硬件&#xff1a; 1、UDP6721直流电源&#xff1a;受控设备 2、moxa串口服务器5150&#xff1a;将UDP6721直流电源设备串口连接转成网络连接 3、香橙派Zero3&#xff1a;运…

最近又考了两个Oracle认证,交一下作业

从Oracle 10g 开始考Oracle的认证&#xff0c;现在已经有15个Oracle的认证了&#xff0c;最近又考了两个Oracle认证&#xff0c;分别是云和AI的。是现在正时髦的技术&#xff0c;又恰恰是我的短板&#xff0c;以考促学&#xff0c;正好系统地学习这两门知识。这两个证书的培训和…

内存学习(2):内存分类与常用概念2(SDRAM与DDR)

SDRAM与DDR的简单概念介绍 SDRAM 定义&#xff1a; 同步动态随机存取内存&#xff08;Synchronous Dynamic Random-Access Memory&#xff0c;简称SDRAM&#xff09;是有一个同步接口的动态随机存取内存DRAM&#xff08;可以参考前文&#xff09;。可以理解为是一种利用同步…

浅谈自动化测试框架开发

在自动化测试项目中&#xff0c;为了实现更多功能&#xff0c;我们需要引入不同的库、框架。 首先&#xff0c;你需要将常用的这些库、框架都装上。 pip install requests pip install selenium pip install appium pip install pytest pip install pytest-rerunfailures pip …

数据结构-顺序表

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线…

初识rust

调试下rust 的执行流程 参考&#xff1a; 认识 Cargo - Rust语言圣经(Rust Course) 新建一个hello world 程序&#xff1a; fn main() {println!("Hello, world!"); }用IDA 打开exe&#xff0c;并加载符号&#xff1a; 根据字符串找到主程序入口&#xff1a; 双击…

python模块的介绍和导入

python模块的介绍和导入 概念 在Python中&#xff0c;每个Python代码文件都是一个模块。写程序时&#xff0c;我们可以将代码分散在不同的模块(文件)中&#xff0c;然后在一个模块中引用另一个模块的内容。 导入格式 1、在一个模块中引用(导入)另一个模块可以使用import语句…

web前端——HTML+CSS实现九宫格

web前端——HTMLCSS实现九宫格 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title&…

大数据毕业设计选题推荐-无线网络大数据平台-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

【JavaEE】JVM 剖析

JVM 1. JVM 的内存划分2. JVM 类加载机制2.1 类加载的大致流程2.2 双亲委派模型2.3 类加载的时机 3. 垃圾回收机制3.1 为什么会存在垃圾回收机制?3.2 垃圾回收, 到底实在做什么?3.3 垃圾回收的两步骤第一步: 判断对象是否是"垃圾"第二步: 如何回收垃圾 1. JVM 的内…

Linux MMC子系统 - 3.eMMC 5.1常用命令说明(1)

By: Ailson Jack Date: 2023.11.05 个人博客&#xff1a;http://www.only2fire.com/ 本文在我博客的地址是&#xff1a;http://www.only2fire.com/archives/162.html&#xff0c;排版更好&#xff0c;便于学习&#xff0c;也可以去我博客逛逛&#xff0c;兴许有你想要的内容呢。…

SpringBoot 将 jar 包和 lib 依赖分离,dockerfile 构建镜像

前言 Spring Boot 是一个非常流行的 Java 开发框架&#xff0c;它提供了很多便利的功能&#xff0c;例如自动配置、快速开发等等。 在使用 Spring Boot 进行开发时&#xff0c;我们通常会使用 Maven 或 Gradle 进行项目构建。 本文将为您介绍如何使用 Maven 将 Spring Boot …

项目实战:新增@Controller和@Service@Repository@Autowire四个注解

1、Controller package com.csdn.mymvc.annotation; import java.lang.annotation.*; Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Inherited public interface Controller { }2、Service package com.csdn.mymvc.annotation; import java.lang.annotation.*…