字符串

187 阅读4分钟

字符串相关函数

输入函数

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')

字符(串)转换

  1. 字符 0~9 转换为数字 0~9: 9 = '9' - '0'

  2. 字符串转换成整数:#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()不会做范围检查,如果超出范围的话,超出上界,则输出上界,超出下界,则输出下界

  1. 整数转化为字符 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*thisnone
cstring(char*)×

字符串题目

字符串查找

13 · 字符串查找

感觉还是考察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

  1. 思维还停留在C中,选择一个字符一个字符比对,完全没有想到利用C++中的string里的函数(当然,也和不熟悉有关)

2.记错string的函数导致溢出。str.substr(pos,length),而不是str.substr(first,last)

3.string重载了运算符,可以直接使用。+, +=, >, >=, <, <=, !=

KMP算法

28. 实现 strStr()

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

统计字符串子串出现次数

CPP33 统计字符串中子串出现次数

这个题目与上一个题目的思路其实是一样的,都是字符串的查找,简单的方法使用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

  1. 字符数组转化为字符串 string stringname(char_Arrayname)

  2. string中find()函数查找无果后返回值为-1

  3. 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

  1. for循环中判断条件str[i] != '\0',该条件是判定字符串结束的重要条件。

  2. str[i+j] == substr[j]

统计字符串中各字母对应个数

CPP50 统计字符串中各字母字符对应的个数

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

  1. 字符函数的使用

去除字符串中重复的字符

CPP49 去除字符串中重复的字符

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;

反转字符串(以单词为单位)

53 · 翻转字符串

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

  1. 怎么从字符串中截取单词:while()循环体部分

  2. 以单词为单位:可用栈,可用vector,也可用string重载的=和+(如上面的代码)

JZ73 翻转单词序列

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=0sk.size()=2,执行函数体中内容,pop掉栈顶元素后,i=1;然后再对for循环中条件2做判断看是否执行函数体,此时sk.size()=1,不再满足条件,所以不执行。也就是说,在栈顶的元素am一直无法获得。【这是一种固定思维,而且比较隐秘,不细心的话很难调试出来】 size()pop()在一起要慎用

比较字符串

55 · 比较字符串

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

  1. find()同样可以查找单个字符

  2. 查找一个删除一个的思路。(属实没想到)

158 · 两个字符串是变位词

本质还是判断字符重复

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

  1. ==的使用:a == b if equal return true else return false

  2. sort()函数的使用(总是想不起用这个函数)

字符串压缩

213 · 字符串压缩

思路:

从当前字符位置往后寻找相同字符,然后创建新字符串。

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

最长公共前缀

14. 最长公共前缀

字符串数组/向量形式

vector<string>str = ["dog", "cat", "panda"]

下标012345
0dog\0
1cat\0
2panda\0

string str[][3] = ["dog", "cat", "panda"]

下标012345
0dog\0\0\0
1cat\0\0\0
2panda\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];
}