字符串相关函数
输入函数
cin.getline(str,len):允许读取包含空格的字符串。它将继续读取,直到它读取至最大指定的字符数,或直到按下了回车键。在输入的末尾增加一个'\0'表示字符串的结束,如果长度小于数组长度,输入所有字符后加'\0',如果长度大于数组长度,截断输入的前面部分再在最后加'\0'
getline():允许读取包含空格的字符串。需#include<string>。
getline()的原型是istream& getline ( istream &is , string &str , char delim ),
其中 istream &is 表示一个输入流,譬如cin;string&str 表示把从输入流读入的字符串存放在这个字符串中(可以自己随便命名,str什么的都可以);char delim表示遇到这个字符停止读入,在不设置的情况下系统默认该字符为'\n',也就是回车换行符(遇到回车停止读入)。
string str;
getline(cin,str);
还可用于分割字符串,如string str = 192.168.0.2~255.255.255.0; string address = "", 可用getline(str, address, '~')进行分割。
cin:接受一个字符串,遇“空格”、“TAB”、“回车”都结束
字符函数
isalpha():判断字符是否是字母('a'-'z' 'A'-'Z')
isdigit():判断字符是否是数字
isspace():判断字符是否是空格、制表符、换行等标准空白
isalnum():判断字符是否是字母或者数字
ispunct():判断字符是标点符号
islower():判断字符是否是小写字母('a'-'z')
isupper():判断字符是否是大写字母('A'-'Z')
字符(串)转换
-
字符 0~9 转换为数字 0~9:
9 = '9' - '0' -
字符串转换成整数:
#include<cstring>
stoi(const string&)
stoi()会做范围检查,默认范围是在int的范围内的,如果超出范围的话则会runtime error
atoi(const char*):一个字符串str,必须调用 c_str()方法 把这个string转换成 const char*类型的
string target;
atoi(target.c_str());
为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式
atoi()不会做范围检查,如果超出范围的话,超出上界,则输出上界,超出下界,则输出下界
- 整数转化为字符
to_string(int/long/longlong/float/double/...)
string.append(), string.push_back, string.insert()
- += 运算符:追加单个参数值
- append 函数:允许追加多个参数值
- push_back 函数:只能追加单个字符
| append() | += | push_back | |
|---|---|---|---|
| 全字符串(string) | √ | √ | × |
| 部分字符串(substring) | √ | × | × |
| 字符数组(char array) | √ | √ | × |
| 单个字符(char) | × | √ | √ |
| 迭代器范围(iterator range) | √ | × | × |
| 返回值(return value) | *this | *this | none |
| cstring(char*) | √ | √ | × |
字符串题目
字符串查找
感觉还是考察C++包和函数的应用:
int strStr(string &source, string &target) {
// Write your code here
return source.find(target);
}
暴力查找
if(source.size() < target.size()) return -1;
//if(!souce.size() || !target.size()) return 0;
if(target == "") return 0;
for(int i = 0; i < source.size();i++)
{
if(source[i] == target[0] && i + target.size() <= source.size() && source.substr(i, target.size()) == target) return i;
}
return -1;
key point
- 思维还停留在C中,选择一个字符一个字符比对,完全没有想到利用C++中的string里的函数(当然,也和不熟悉有关)
2.记错string的函数导致溢出。str.substr(pos,length),而不是str.substr(first,last)
3.string重载了运算符,可以直接使用。+, +=, >, >=, <, <=, !=
KMP算法
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.size(), m = needle.size();
if (m == 0) {
return 0;
}
vector<int> pi(m);
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && needle[i] != needle[j]) {
j = pi[j - 1];
}
if (needle[i] == needle[j]) {
j++;
}
pi[i] = j;
}
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && haystack[i] != needle[j]) {
j = pi[j - 1];
}
if (haystack[i] == needle[j]) {
j++;
}
if (j == m) {
return i - m + 1;
}
}
return -1;
}
};
统计字符串子串出现次数
这个题目与上一个题目的思路其实是一样的,都是字符串的查找,简单的方法使用string的函数来完成。但本题的难点在于,题目给的是char数组而非string。很容易陷入固定思维,一个字符一个字符的来比较,同时,字符串输入用的是cin.getline(),不熟悉的话很容易分不清数组末尾是否有\0。
思路1:
字符数组转化为string,然后运用函数解决。
string str1(str);
string str2(substr);
int index = 0;
while(str1.find(str2,index) != -1)
{
count++;
index = str1.find(str2,index) + 1;
}
cout << count << endl;
key point
-
字符数组转化为字符串
string stringname(char_Arrayname) -
string中find()函数查找无果后返回值为-1
-
index = str1.find(str2,index) + 1,摒弃一个字符一个字符比对的思维!!
思路2:
一个字符一个字符比对
for(int i = 0; str[i] != '\0'; i++)
{
bool flag = true;
for(int j = 0; substr[j] != '\0'; j++)
{
if(str[i+j] != '\0' && str[i+j] == substr[j]) continue;
else
{
flag = false;
break;
}
}
if(flag) count++;
}
cout << count << endl;
key point
-
for循环中判断条件
str[i] != '\0',该条件是判定字符串结束的重要条件。 -
str[i+j] == substr[j]
统计字符串中各字母对应个数
map <char,int> mp;
for(int i = 0; str[i] != '\0'; i++)
{
if(isalpha(str[i])) mp[str[i]]++;
}
for(auto it = mp.begin(); it != mp.end(); it++)
{
cout<< it->first << ':' << it->second << endl;
}
key point
- 字符函数的使用
去除字符串中重复的字符
set<char> st;
for(int i = 0; str[i] != '\0'; i++) st.insert(str[i]);
for(auto it = st.begin(); it != st.end(); it++) cout<<*it;
反转字符串(以单词为单位)
for(int i = 0; s[i] != '\0'; i++)
{
bool flag = false;
string temp = "";
while(i < s.size() && s[i]!= ' ')
{
temp.push_back(s[i++]);
flag = true;
}
if(flag) ans = ' ' + temp + ans; //key point
}
ans.erase(0,1);
key point
-
怎么从字符串中截取单词:
while()循环体部分 -
以单词为单位:可用栈,可用vector,也可用string重载的=和+(如上面的代码)
stack<string> sk;
for(int i = 0; str[i] != '\0'; i++)
{
string tmp = "";
bool flag = false;
while(i < str.size() && str[i] != ' ')
{
tmp.push_back(str[i++]);
flag = true;
}
if(flag) sk.push(tmp);
}
string ans = "";
/*wrong code*/
for(int i = 0 ; i < sk.size();i++)
{
ans = ans + ' ' + sk.top();
sk.pop();
}
/*right code*/
int len = sk.size();
for(int i = 0 ; i < len;i++)
{
ans = ans + ' ' + sk.top();
sk.pop();
}
ans.erase(0,1);
return ans;
}
key point
提取单词的思路是一样的,主要记录一下关于栈的问题。我一开始写的代码是wrong code部分的函数体,然后错误是无法循环,当测试数据为"am I"的时候,只输出I。测试了前面提取单词,发现没有问题。测试了好久发现问题从处在i<sk.size()中,在循环过程中,sk.size()是一直在变的,因为循环体中有sk.pop()。就拿上个测试数据为例,第一次循环时i=0,sk.size()=2,执行函数体中内容,pop掉栈顶元素后,i=1;然后再对for循环中条件2做判断看是否执行函数体,此时sk.size()=1,不再满足条件,所以不执行。也就是说,在栈顶的元素am一直无法获得。【这是一种固定思维,而且比较隐秘,不细心的话很难调试出来】 size()和pop()在一起要慎用
比较字符串
bool compareStrings(string &A, string &B) {
// write your code here
for(char ch:B)
{
if(A.find(ch) == -1) return false;
A.erase(A.find(ch),1);
}
return true;
}
key point
-
find()同样可以查找单个字符 -
查找一个删除一个的思路。(属实没想到)
本质还是判断字符重复
bool anagram(string &s, string &t) {
// write your code here
if(s.size() != t.size()) return false;
if(s == "" || t == " ") return false;
for(char ch: s)
{
if(t.find(ch) == -1) return false;
else t.erase(t.find(ch),1);
}
if(!t.size()) return true;
else return false;
}
思路2
bool anagram(string &s, string &t)
{
// write your code here
sort(s.begin(),s.end());
sort(t.begin(),t.end());
return s==t;
}
key point
-
==的使用:a == b if equal return true else return false -
sort()函数的使用(总是想不起用这个函数)
字符串压缩
思路:
从当前字符位置往后寻找相同字符,然后创建新字符串。
string compress(string &originalString) {
// write your code here
int origi_len = originalString.size();
string ans;
for(int i = 0; originalString[i] != '\0'; )
{
int count = 1;
ans.push_back(originalString[i]);
for(int j = i + 1; originalString[j] != '\0'; j++)
{
if(originalString[i] == originalString[j]) count++;
else break;
}
i += count; //important
ans += to_string(count);
}
if(ans.size() >= origi_len) return originalString;
else return ans;
}
最长公共前缀
字符串数组/向量形式:
vector<string>str = ["dog", "cat", "panda"]
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | d | o | g | \0 | ||
| 1 | c | a | t | \0 | ||
| 2 | p | a | n | d | a | \0 |
string str[][3] = ["dog", "cat", "panda"]
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 0 | d | o | g | \0 | \0 | \0 |
| 1 | c | a | t | \0 | \0 | \0 |
| 2 | p | a | n | d | a | \0 |
每个string是横向存储而非纵向存储。
string longestCommonPrefix(vector<string>& strs) {
if(!strs.size()) return "";
/*二维数组实现*/
/*
---> i (length)
f l o w e r
f l o w
f l i g h t
*/
int len = strs.size();
int length = strs[0].size();
for(int i = 0; i < length; i++)
{
char ch = strs[0][i];
for(int j = 1; j < len; j++)
{
if(i == strs[j].size() || ch != strs[j][i]) return strs[0].substr(0,i);
}
}
return strs[0];
}