【Day3】小结所有常用的string类技巧

目录:

  • 1.解决超时技巧
  • 2.逆序迭代器rit和四个常见参数 .begin(),   .end()  ,   .rbegin() ,   .rend()的使用方法 
  • 3.string类中函数find()的用法
  • 4.string类中函数erase()的用法
  • 5.string类中函数substr()的用法
  • 6.使用stack结构的易错点
  • 7.string类重载的符号
  • 8.逆序通用算法
  • 9.memset对于结构体并不通用
  • 10.明确二维数组的线性储存方式

1.上午发现了一个超时的原因就是要把数据的输入放到while()的括号里面,否则超时

就比如对下面这道判断亲和数的题目:

	while(cin >> num1 >> num2){
		if(sum(num1) == num2&&sum(num2) == num1){
			cout<<"YES"; 
		}
		else{
			cout<<"NO";
		}
		cout << endl;
	}

上面是不超时的版本,下面是原来超时的版本

	while(1){
                cin >> num1 >> num2;
		if(sum(num1) == num2&&sum(num2) == num1){
			cout<<"YES"; 
		}
		else{
			cout<<"NO";
		}
		cout << endl;
	}

2.使用迭代器遍历string类

首先是string类中也有begin()和end();

也有push_back 和 pop_back();

当然上面是和vector进行对比,不是跟queue对比(因为queue使用push()和pop())

同样地可以使用迭代器遍历

顺序迭代器的写法和map中相同

在已经引入了命名空间的情况下,我们有:

	string str = "aaaaaa";
	cin >> str;
	for(string :: iterator it = str.begin() ; it != str.end() ; it++){
		cout << *it; 
	}	

那么这个demo的效果就是将输入的数据原样打印出来,当然注意string输入时空格的截断性

要注意的是:

在c++tutorial中有写:

Returns an iterator pointing to the past-the-end character of the string.
 

The past-the-end character is a theoretical character that would follow the last character in the string. It shall not be dereferenced.

由此我们知道这样的尾部元素的下一位可能实际上并不存在,但是它就是str.end()

值得注意的是,这里的str.begin() str.end() str.rbegin() str.rend()都是指针,所以这种写法是绝对不允许的

str.front() = 'H';//试图把一个字符型常量赋给一个指针,会报错

真正的尾部的元素应该是rebgin()这里的r代表reverse

例如我们可以写:(利用逆序迭代器 rit 也是++,同理此时对于string类的开头,我们应该使用的是rend()此时相当于是首元素前面那个)

	string str = "abcdefg";
	for(string :: reverse_iterator rit = str.rbegin() ; rit != str.rend() ; rit++ ){
		cout << *rit << " ";
	}

3.对string进行搜索操作

这里只写find()操作,其他所有的查找函数放在另一篇里面总结

只做find()的说明

Find content in string

Searches the string for the first occurrence of the sequence specified by its arguments.

When pos is specified, the search only includes characters at or after position pos, ignoring any possible occurrences that include characters before pos.

Notice that unlike member find_first_of, whenever more than one character is being searched for, it is not enough that just one of these characters match, but the entire sequence must match.

find()函数的返回值:

类型是size_t(也就是string :: size_type),如果查找到了,那么返回这样一个可以使用的size_t类型的变量

否则返回string:: npos (no position缩写)

也就是说我们应该用这样的size_t变量接收string类的下标(见示例)

string :: iterator it = str.find('7');//这种用法报错,应该用 size_t  position = find('7');

这里的position是我定义的用来接收发现的元素的下标的变量名称

但其实,使用int 类型变量去接收这个下标值也合法,但是无法接收错误信息(找不到该字母或序列),下面会说到

也就是 int pos = find('7');//编译通过

            cout << pos << endl; //输出6

 其有两个用法:

string (1)
size_t find (const string& str, size_t pos = 0) const noexcept;
character (4)
size_t find (char c, size_t pos = 0) const noexcept;

可看到其中的默认参数pos是0,意思是默认从头开始找(这里容易出错的地方还是string可以看成数组,而数组第一个元素的下标是0!!!)

例子:

例1:

输出4

容易错的地方‘13345’这是绝对不允许的,但是不报错!!!!

例2:(不从第一个元素开始查找,传入两个参数)

#include <bits/stdc++.h>
using namespace std;
int main(){
	string str = "123456789 abcd 123 abcd 456";
	size_t Found_ch = str.find('a' , 11);
	cout << "size_t " << Found_ch << endl;
	size_t Found_str = str.find("abcd" , 15);
	cout << "pos size_t " << Found_str << endl; 
	return 0;
}

//在string里面也有front()和back()这非常类似于queue,它们都返回引用,可以修改

C11标准,但是我的dev-c 5.7.1并不支持

4.对于erase()函数这个有两个功能:

常见的错误与解决方法:

1.这种用法绝对是错的,不符合下面的任何一种接口

2.这个就是对的,可以使用初始下标加长度(下表第一种用法)

3.所以问题可以使用str.erase('7'的下标,1);解决(或者使用下面的迭代器方法解决)

先说其接口:

这里第一种用法,虽然数据类型是size_t 但是由于我们不考虑异常情况,直接传int型变量没有问题

 (1)
 string& erase (size_t pos = 0, size_t len = npos);
character (2)
iterator erase (const_iterator p);
range (3)
iterator erase (const_iterator first, const_iterator last);

sequence:删除一个子列,给出的参数是这个子列的开头的下标和子列的长

character:必须传入一个正序迭代器(使用迭代器遍历,并对满足要求的*it使用erase方法,这时我们使用erase(it))

range:也是传入两个正序迭代器,这样就把中间及两端点全删掉

再说其返回值:

The sequence version (1) returns *this.
The others return an iterator referring to the character that now occupies the position of the first character erased, or string::end if no such character exists.
 

Member type iterator is a random access iterator type that points to characters of the string.

及返回值为一个指向被删除的一段空间的首地址的迭代器指针

a.擦除从某个已知位置(需要给出指向这个字符的迭代器作为参数)的字符然后缩小长度

语法结构是:

str.erase(iterator_sth);

这里的iterator_it用来代表指向sth的指针

具体的例子:

#include <iostream>
#include <string>
using namespace std;
int main(){
	string str = "ababab";
	str.erase(str.begin());
	cout << str << endl; //输出babab
	for(string :: iterator it = str.begin() ; it != str.end() ; it++ ){
		if((*it) == 'a'){
			str.erase(it);
		}
	}
	cout << str;//输出 bbb
	return 0;
}

特别要注意的是:这种寻找某个确定元素并删除的方法是非常好用但是!!!!!!

不能使用逆序迭代器进行删除!!!!

也就是str.erase(rit); //没有这种用法,报错

第11行错误,其他两行正确,上面已经说明.end()本身就是假想出来的结束为止的下一个

同理magic.erase(magic.rbegin());和magic.erase(magic.rend());也是错误的

因为只能传入正向迭代器

b.擦除某一段字符

对于擦除某一段字符,有两种实现方法

用法是:

1.可以传入两个参数start , len,此时删的就是从下标start开始持续长度为len的一段字符串

2.可以传入两个正序迭代器first和last,如果使用str.erase(first,length);

Iterators specifying a range within the string] to be removed: [first,last). i.e., the range includes all the characters between first and last, including the character pointed by first but not the one pointed by last.

也就是删[first,last)

例子:

#include <iostream>
#include <string>
using namespace std;
int main(){
	string str = "11111[1234567899876543211]23456789098876543322";
	string :: iterator first , second;
	for(string :: iterator it = str.begin() ; it != str.end() ; it++){
		if((*it) == '['){
			first = it;
		}
		else if((*it) == ']'){
			second = it;
		}
	}
	str.erase(first,second);
	cout << str;
	return 0;
}

输出结果含有‘]’

5.substr

作用是根据已有的串构建子串

首先看其接口:

string substr (size_t pos = 0, size_t len = npos) const;

再看其返回值:

string object with a substring of this object.

即返回一个字符串

所以有三种用法:

1.使用str.substr(pos,len);

它的参数列表的与erase()基本一致,比如这里与erase()的第一种用法参数形式完全相同

pos

Position of the first character to be copied as a substring.
If this is equal to the string length, the function returns an empty string.
If this is greater than the string length, it throws out_of_range.
Note: The first character is denoted by a value of 0 (not 1).//这个一定要特别注意!!!!

len

Number of characters to include in the substring (if the string is shorter, as many characters as possible are used).
A value of string::npos indicates all characters until the end of the string.

	string str = "1234567890";
	string str1 = str.substr(2,3);
	cout << str1;//输出3,4,5 

2.使用默认的参数,直接从pos开始构造到末尾(此时len默认取npos,而这个为最大值)

此时写法为 substr(pos); // pos本质是一个整型数字,代表构造的起点(包含该起点对应元素),虽然是size_t 类型,但是用int型没有问题

但是对于异常情况,int类型无法接收,所以还是使用std类里面自带的数据类型size_t最保险

这就是一个使用int类型没有办法判断不在里面的情况的例子

那么正确的写法应该是:

当然,如果开始引入了命名空间

即使用了using namespace std;此时可以省略std ::

	string str = "1234567890";
	size_t pos = str.find('567');
	if(pos == string :: npos){
		cout << "not found" << endl;
	}
	else{
		cout << "position of it is " << pos << endl; 
	}
string substr (size_t pos = 0, size_t len = npos) const;

首先明确它的两个参数的意义(而且带有默认值)

对于第一个参数pos: (就是返回当前准备开始构造的值)

对于第二个参数npos:(就是返回当前容器中size_t的最大值)

故第三种用法是什么参数都不加,相当于复制了当前整个语句(不如使用等号);

#include <iostream>
#include <string>
using namespace std;
int main(){
	string str = "1111122222333333";
	string str1 = str.substr(0);
	cout << str1 << endl;//相当于复制了str
	string str2 = str.substr();
	cout << str2 << endl;//也相当于复制str 
	string str3 = str.substr(5,5);
	cout << str3;  
	return 0;
}

portion:遗产

npos is a static member constant value with the greatest possible value for an element of type size_t.

6.使用stack的易错点

在处理括号匹配问题的时候,开始向栈中加入元素,然后弹出,弹出前进行修改元素的值,但是外面的元素没变

意思是:

#include <iostream>
#include <stack>
using namespace std;
int main(){
	stack <int> s;
	int num = 100;
	s.push(num);
	s.top() = 200;
	s.pop();
	cout << num;
	return 0;
}
//入栈元素非元素本身
//所以输出值仍为100 

7.string 类重载的符号

有加无减(有加等和加没有减等和减)

8.逆序通用算法

使用algorithm库中的reverse()函数

例如对于一个字符串

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main(){
	string str1 = "abcdefg";
	reverse(str1.begin(),str1.end());
	cout << str1;
	return 0;
}

10.memset对于结构体并不通用(对int和bool构成的可以用)

虽然能过,但有时候会报错,老实多打几行算了

#include <iostream>
#include <string.h>
using namespace std;
struct Num{
	bool flag;
	int num;
};
int main(){
	Num a[10][10];
	memset(a , 0 , sizeof(a));	
	for(int i = 0 ; i < 10 ; i++){
		for(int j = 0 ; j < 10 ; j++){
			cout << a[i][j].num;
			if(j == 9){
				continue;
			} 
			cout << " ";
		}
		cout << endl;
	}
	cout << endl;
	for(int i = 0 ; i < 10 ; i++){
		for(int j = 0 ; j < 10 ; j++){
			cout << a[i][j].flag;
			if(j == 9){
				continue;
			} 
			cout << " ";
		}
		cout << endl;
	}
	return 0;
}

10.明确二维数组的线性储存方式

#include <iostream>
using namespace std;
int main(){
	int a[10][10];
	cout << a[0] << endl;
	cout << &a[0][0] << endl;
	return 0;
}

所以对于a[m][n]

可以根据a[0]+i*n+j或者&a[0][0]+i*n+j计算出a[i][j]的地址

猜你喜欢

转载自blog.csdn.net/chenhanxuan1999/article/details/81050088