zoukankan      html  css  js  c++  java
  • C,C++语法基础 | 字符串 | 05

    字符串 | 05

    字符串与整数的联系 -- ASCII码

    每个常用的字符都对应一个0~127的数字,二者之间可以相互转化.

    #include<iostream>
    using namespace std;
    
    int main(){
        char c = 'a';
        cout << (int)c << endl; // 97
            
        int a = 66;
        cout << (char)a << endl; // B
        
        return 0;
    }
    

    常用的ASCII码值:

    • '0': 48 '0'`'9'`是4857
    • 'A': 65
    • 'a': 97

    字符串可以参与运算,运算时会将其当做整数.

    #include<iostream>
    using namespace std;
    
    int main(){
        int a = 'B' - 'A'; // 66 - 65
        int b = 'A' * 'B'; // 65 * 66
        char c = 'A' + 2; // 65 + 2
        
        cout << a << endl;
        cout << b << endl;
        cout << c << endl;
        
        
        return 0;
    }
    

    字符数组的定义

    字符串就是字符数组加上结尾符''

    可以使用字符串来初始化字符数组,但此时要注意,每个字符串结尾会暗含一个字符,因此字符数组的长度至少要比字符串的长度多1.

    // 前两种是正常的数组初始化
    char a1[] = {'C','+','+'}; // 列表初始化,没有空字符
    char a2[] = {'C','+','+',''}; // 列表初始化,含有显示的空字符
    // 后两种使用双引号进行初始化
    char a3[] = "C++"; // 自动添加表示字符串结尾的空字符
    char a4[6] = "Daniel"; // 错误: 没有空间可存放空字符!
    

    字符串的输入输出

    char a[100]; // 定义一个字符数组来存储字符串
    scanf("%s",a); // 这里字符串的格式为 %s, 还有就是数组是不用&取地址符号的,数组名本身就是一个地址
    scanf("%s",a+1); // 从a[1]开始存储
    cin >> s + 2; // 从a[2]开始存储
    
    // 输入多个字符串,使用空格隔开就可以读入
    char s1[100],s2[100];
    scanf("%s %s",s1,s2); // 读入 abc def
    

    但是需要注意的是,普通读入字符串的方法,遇到空格或者回车就会停止读入.

    下面有几种方式可以读入一整行字符串,可以读入包含空格的字符

    // 字符数组整行读入
    gets(s); // 这个函数已经被淘汰了,现在在使用的话就会出现CE
    fgets(s,最多读入多少字符,stdin); // 一般中间数字保证这一行字符足够读入即可
    cin.getline(s,最多读入多少字符); 
    
    // 字符串整行读入
    getline(cin,s);
    
    

    输出字符串

    // 下面3种都是等价的字符串输出
    puts(s); // 直接输出字符串并换行
    printf("%s
    ",s);
    cout << s << endl;
    

    字符数组的常用操作

    首先需要引入头文件#include<string.h>或者#include<cstring>

    strlen(s); //求字符串的长度,注意长度就是明面上的长度,不包含最后一个``
    strcpy(a,b); // 把字符串b复制给字符串a,是后面复制给前面的
    strcmp(a,b); // 按照字典序比较字符串a和字符串b,依次输出 -1,0,1
    

    还有就是字符串的循环,一般是不求长度的,利用结尾的

    // 求字符串长度
    len = strlen(s);
    for(int i=0;i<len;i++);
    
    // 利用结尾的''
    for(int i=0;s[i];i++);
    

    标准库类型 string的定义

    比赛的时候我们很少自己定义字符数组来存储字符串,80%的情况都是定义string来处理.

    string是一个可变长的字符串,比字符数组更加好用. 可以很方便的执行字符串的拼接等操作.

    首先需要引入#include<string>

    string s1; // 默认初始化,s1是一个空字符串
    string s2=s1; // s2是s1的副本
    string s3="hiya"; // s3是该字符串字面值的副本
    string s3(10,'c'); // s4的内容和是 cccccccccc
    

    需要注意的是,string使用scanf/printf都比较麻烦,所以建议使用string就直接使用cin/cout

    string的empty()和size()

    这是string两个常用的方法.

    需要注意, string.size()的时间复杂度是O(1)的, 因为类中专门有一个变量来进行长度的存储,而strlen()的时间复杂度是O(n)

    string s = "abc";
    cout << s.empty() << endl; // 0
    cout << s.size() << endl; // 3
    

    string的比较操作

    string支持>,<,=,>=,<=,==,!=等所有比较操作,按照字典序进行比较.

    string的加法

    c++string可以和Java一样,非常方便的进行字符串的拼接操作,一样都是先把其他字符转换为string类型

    string s1 = "hello, ",s2="world
    ";
    string s3 = s1 + s2; // s3 的内容是 hello, world
    
    // 当然还可以加上字符类型
    s3 += '!'; // s3 的内容是 hello, world
    !
    

    需要主要+=的使用场景,就是必须左右两边至少需要保证有一个string类型,否则就会出错

    string s = "abc" + '!'; // 这是错误的! 因为字符串是不能相加的,可以进行相加的是string
    

    处理string对象中的字符

    可以将string对象当成字符数组来处理

    string s = "hello, world";
    for(int i=0;i<s.size();i++)
    

    或者使用基于范围的for语句

    string s = "hello world";
    for(char c:s)cout << c << endl; // 这个很类似增强型的for
    for(char &c:s)c='a'; // 直接进行取地址
    // 当然不可以不具体指明,使用auto
    for(auto &c:s)c='a';
    

    stringstream

    引入头文件#inlcude<sstream>

    stringstream的用处就是从字符串中提取出来需要的各种信息.

    ssincin基本是完全等价的.

    #include<bits/stdc++.h>
    using namespace std;
    
    
    int main(){
        string s;
        getline(cin,s);
        stringstream ssin(s); // 把字符串初始化为stringstream
        int a,b;
        string str;
        double c;
        
        // 可以使用 ssin 实现 cin 的功能  
        ssin >> a >> b >> str >> c;
        cout << a << endl << b << endl << str << endl << c << endl;
        return 0;
    }
    

    string常用函数

    string.back()返回最后一个字符

    string.pop_back() 删除最后一个字符

    string.substr(起始下标,长度) 截取字符串子串

    string.find(子串) 查找判断字符或者子串是否在字符串中,如果存在,则返回索引,负责返回一个不确定数s.find(xx)>=0 && s.find(xx)<s.size()

    注意: s.size()最好是赋值给一个len,因为比s.size()小的数减去s.size()不能得到一个正确答案.

    时间统计函数

    引入#include<ctime>

    int start_time = clock();
    for(int i=0;i<10000;i++); // 制造一个循环
    cout << (clock() - start_time) / 1000 << endl;
    

    习题五

    字符串长度

    string.size(), strlen(s)

    #include<iostream>
    using namespace std;
    
    int main(){
        
        string s;
        getline(cin,s);
        cout << s.size() << endl;
        
        return 0;
    }
    

    字符串中的数字个数

    #include<cstdio>
    #include<iostream>
    #include<string>
    
    using namespace std;
    
    int main(){
        string s;
        getline(cin,s);
        int cnt = 0;
        for(auto c:s){
            if(c >= '0' && c <= '9'){
                cnt++;
            }
        }
        cout << cnt << endl;
        
        return 0;
    }
    

    注意: 这是读入一行有空格的字符串. 字符可以直接和字符进行比较,就是做int进行处理.

    循环相克令

    #include<iostream>
    #include<cstdio>
    #include<string>
    
    using namespace std;
    
    int get_num(string s){
        if(s=="Hunter")return 0;
        if(s=="Gun")return 1;
        if(s=="Bear")return 2;
    }
    
    int main(){
        
        int n;
        cin >> n;
        
        while(n--){ // 循环的写法
            string s1,s2;
            cin >> s1 >> s2;
            int x=get_num(s1),y=get_num(s2);
            
            if((x+1)%3==y){
                cout << "Player1" << endl;
            }else if(x == y){
                cout << "Tie" << endl;
            }else{
                cout << "Player2" << endl;
            }
        }
        
        return 0;
    }
    

    循环的写法:

    int n;
    cin >> n;
    while(n--){
        ...
    }
    

    如果直接写的话,两个人都要有3种可能,那么最坏的情况就要写9个判断.

    可以这么处理,用3进制处理.

    猎人: 0, 枪: 1 : 熊: 2

    (x+1)%3 == y 赢
    (x+1)%3 < y 输
    x == y 平
    

    字符串加空格

    #include<iostream>
    #include<string>
    #include<cstdio>
    using namespace std;
    
    
    int main(){
        string s;
        getline(cin,s);
        for(auto c:s)cout << c << " ";
        return 0;
    }
    

    替换字符

    #include<bits/stdc++.h>
    using namespace std;
    
    
    int main(){
        string s,a,b;
        getline(cin,s);
        cin >> a >> b;
        stringstream ssin(s); // 把字符串初始化为stringstream
        string s2;
        while(ssin >> s2){
            if(s2 == a){
                cout << b << " ";
            }else{
                cout << s2 << " ";
            }
        }
        return 0;
    }
    

    字符串插入

    #include<iostream>
    #include<string>
    #include<cstdio>
    using namespace std;
    
    int main(){
        string s1,s2;
        while(cin >> s1 >> s2){
            int max = -1;
            for(auto c:s1)max = max > c ? max : c;
            for(int i=0;i<s1.size();i++){
                if(s1[i] == (char)max){
                    cout << (s1.substr(0,i+1) + s2 + s1.substr(i+1)) << endl;
                    break;
                }
            }
        }
        
        return 0;
    }
    

    有若干行数据,读入多组数据

    string a,b;
    while(cin >> a >> b){
        ...
    }
    

    还有就是使用了字符串截取函数string.substr(起始坐标,截取长度)

    只出现一次的字符

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    // 10W最好就开成全局,方法内栈内存会限制为1M,会爆
    // 全局变量为堆内存,没有设限
    char s[100000+10]; // 数组都是多开10个,保证安全
    
    int main(){
        cin >> s;
        int cnt[26+10] = {0}; 
        int len=strlen(s);
        for(int i=0;i<len;i++){
            cnt[s[i]-'a']++;
        }
        for(int i=0;i<len;i++){
            if(cnt[s[i]-'a']==1){
                cout << s[i] << endl;
                return 0;
            }
        }
        cout << "no" << endl;
        
        return 0;
    }
    

    使用一个数组来记录每个字符出现的次数,然后再遍历一次确定第一次只出现一次的字符.

    字符串匹配

    #include<iostream>
    #include<string>
    
    using namespace std;
    
    int main(){
        double k;
        string s1,s2;
        cin >> k >> s1 >> s2;
        int cnt = 0;
        for(int i=0;i<s1.size();i++){
            if(s1[i] == s2[i])cnt++;
        }
        cout << ((double)cnt / s1.size() >= k ? "yes" : "no") << endl;
        return 0;
    }
    

    注意: intdouble进行除法的话,首先要将int转为double

    忽略大小写比较字符串大小

    #include<iostream>
    #include<cstring>
    using namespace std;
    
    int main(){
        string s1,s2;
        getline(cin,s1);
        getline(cin,s2);
        // 都转化为小写字母处理
        for(int i=0;s1[i];i++){
            if(s1[i]>='A' && s1[i]<='Z')s1[i] += 32;
        }
        for(int i=0;s2[i];i++){
            if(s2[i]>='A' && s2[i]<='Z')s2[i] += 32;
        }
        int res = s1.compare(s2);
        if(res == 0)cout << "=";
        else if(res < 0)cout << "<";
        else cout << ">";
        return 0;
    }
    

    去掉多余空格

    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
        string s;
        while(cin >> s){
            cout << s << " ";
        }
        return 0;
    }
    

    这种空格使用cin读入,会自动过滤的,所以只要读入然后输出即可.

    如果是自己手动探路的,就会使用到另外一类的双指针

    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
        string s1,s2;
        getline(cin,s1);
        for(int i=0;i<s1.size();i++){
            if(s1[i]!=' '){
                s2 += s1[i]; 
            }else{
                s2 += ' '; // 加上第一个空格
                int j = i;
                while(j < s1.size() && s1[j] == ' ')j++;
                i = j - 1; // 抵消后面的i++
            }
        }
        cout << s2 << endl;
        
        return 0;
    }
    

    探路算法(经常用)

    int j = i;
    while(j<s1.size() && j==' ')j++; 
    i = j-1;
    

    信息加密

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<string.h>
    
    using namespace std;
    
    int main(){
        string s;
        getline(cin,s); // 注意,这是输入一行有空格的字符串
        for(auto &c:s){
            if((c>='a'&&c<'z') || (c>='A')&&(c<'Z'))c = (char)(c+1);
            else if(c=='z')c='a';
            else if(c=='Z')c='A';
            
        }
        cout << s << endl;
        
        return 0;
    }
    

    这里需要注意的是输入的string是有空格的.

    输出字符串

    #include<bits/stdc++.h>
    using namespace std;
    
    
    int main(){
        string s1,s2;
        getline(cin,s1);
        
        for(int i=1;i<s1.size();i++){
            s2 += s1[i] + s1[i-1]; // 字符直接相加,如果是char类型会自动转为char
        }
        s2 += (s1[0] + s1[s1.size()-1]);
        cout << s2 << endl;
        
        return 0;
    }
    

    其实有问题的还是循环的问题,这种循环其实只要取模就可以了

    像下面这种循环,可以不需要特判,直接取模即可

        for(int i=1;i<s1.size();i++){
            s2 += s1[i] + s1[i-1]; // 字符直接相加,如果是char类型会自动转为char
        }
        s2 += (s1[0] + s1[s1.size()-1]);
    
        for(int i=0;i<s1.size();i++){
    		s2 += s1[i] + s1[(i+1)%s1.size()];
        }
    

    单词替换

    #include<bits/stdc++.h>
    using namespace std;
    
    
    int main(){
        string s,a,b;
        getline(cin,s);
        cin >> a >> b;
        stringstream ssin(s); // 把字符串初始化为stringstream
        string s2;
        while(ssin >> s2){
            if(s2 == a){
                cout << b << " ";
            }else{
                cout << s2 << " ";
            }
        }
        return 0;
    }
    
    

    字符串中最长的连续出现的字符

    
    

    这个我原来想的还是使用数组来存次数,然后找到最大的. 但是这种连续子串天然可以使用双指针算法.

    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
        int n;
        cin >> n;
        while(n--){
            string s;
            cin >> s;
            // 存放最大次数和最大次数的字符
            int cnt = 0;
            char c;
            for(int i=0;i<s.size();i++){
                int j=i;
                while(j < s.size() && s[i] == s[j])j++;
                if(cnt < j - i)cnt = j - i,c = s[i];
                i = j -1;
            }
            cout << c << " " << cnt << endl;
        }
        
        return 0;
    }
    

    双指针算法的练习 -- "扣单词"

    比如"I'm a teacher, I from China!" 把单词一个一个扣出来

    #include<iostream>
    #include<string>
    using namespace std;
    
    int main(){
        string s;
        getline(cin,s);
        for(int i=0;i<s.size();i++){
            int j=i;
            while(j < s.size() && s[j]!=' ')j++;
            cout << s.substr(i,j-i) << endl;
            i = j;
        }
        
        return 0;
    }
    

    最长单词

    #include<bits/stdc++.h>
    using namespace std;
    
    int main(){
        string res,s;
        while(cin >> s){
            if(s.back() == '.')s.pop_back(); 
            if(res.size() < s.size())res = s; // 注意这里等于是不会替换的,也就保证了第一个
        }
        cout << res << endl;
        return 0;
    }
    

    倒排单词

    #include<iostream>
    #include<string>
    using namespace std;
    
    int main(){
        string ss[100];
        int n=0;
        while(cin >> ss[n])n++;
        for(int i=n-1;i>=0;i--){
            cout << ss[i] << " ";
        }
        return 0;
    }
    

    如何是按照单词为单位的话,那就需要定义字符串数组.

    // 循环读入字符串数组
    string ss[100];
    int n=0;
    while(cin >> ss[n])n++;
    

    字符串移位包含问题

    #include<iostream>
    using namespace std;
    
    int main(){
        string s1,s2;
        cin >> s1 >> s2;
        // 这里保证了第一个字符串是最长的
        // swap() 可以交换所有类型的变量
        if(s1.size() < s2.size())swap(s1,s2); 
        for(int i=0;i<s1.size();i++){
            s1 = s1.substr(1) + s1[0];   
            int f = s1.find(s2);
            if(f>=0 && f<s1.size()){ // 这里使用了查找函数
                cout << "true";
                return 0;
            }
        }
        cout << "false";
        
        return 0;
    }
    

    首先一个是循环串的移位,y总也是使用的字符串拼接的方式.

        for(int i=0;i<s1.size();i++){ // 循环移位
            s1 = s1.substr(1) + s1[0]; 
    

    还有就是保证第一个字符串是最大的,可以使用swap(),swap()任何类型都能够进行交换

    string s1,s2;
    if(s1.size() < s2.size())swap(s1,s2); 
    

    字符串乘方

    #include<iostream>
    using namespace std;
    
    int main(){
        string s1;
        while(cin>>s1,s1!="."){
            int len = s1.size();
            for(int n=len;n;n--){ // 这里的n是重复的次数
                if(len % n ==0){ // // 循环节的长度一定是字符串长度的约数
                    int m = len / n;
                    string s2 = s1.substr(0,m); // 这个就是循环节,都是从头开始的
                    string s3;
                    for(int i=0;i<n;i++)s3 += s2;
                    if(s1 == s3){
                        cout << n << endl;
                        break;
                    }
                }
                
            }
        }
    }
    

    首先一个是读入问题,最后一个以.结束,逗号表达式从左到右求值,最后的值作为返回值

    string s;
    while(cin >> s,s!='.'){
        ...
    }
    

    字符串的最大跨距

    #include<iostream>
    using namespace std;
    
    
    int main(){
        string s,s1,s2;
        char c;
        while(scanf("%c",&c),c!=',')s+=c;
        while(scanf("%c",&c),c!=',')s1+=c;
        cin >> s2;
        
        // 找到最左边和最右边的位置
        int l = s.find(s1),r = s.rfind(s2);
        int s1_len = s1.size();
        if(l>=0 && l<s.size() && r>=0 && r<s.size() && r - l - s1_len>=0){
            cout << r - l - s1_len ; 
        }else{
            cout << "-1";
        }
        return 0;
    }
    

    首先是读入的问题,如果是以逗号分隔的读入,那么就可以使用单个字符一个一个读入.

        string s,s1,s2;
        char c;
        while(scanf("%c",&c),c!=',')s+=c;
        while(scanf("%c",&c),c!=',')s1+=c;
        cin >> s2;
    

    这个题目需要使用到string.find()string.rfind(),需要注意的是,其实都是从左开始找的.

    还有就是string.size()的问题,如果是需要进行减法运算的话,建议赋值给一个len进行存储.

    最长公共字符串后缀

    #include<iostream>
    using namespace std;
    
    const int N = 210;
    string ss[N];
    
    int main(){
        int n;
        while(cin >> n,n){
            int len = 0x7ffffff;
            for(int i=0;i<n;i++){
                cin >> ss[i];
                if(len > ss[i].size())len = ss[i].size();
                
            }
            while(len){// 从len开始枚举,到0直接输出即可
                bool success = true;
                for(int i=1;i<n;i++){ // 从第二个字符串开始枚举,因为都是跟第一个字符串进行比较
                    bool is_same = true;  // 首先假设是相同的
                    for(int j=1;j<=len;j++){
                        if(ss[0][ss[0].size()-j]!=ss[i][ss[i].size()-j]){
                            is_same = false;
                            break;
                        }
                    }
                    if(!is_same){
                        success = false;
                        break;
                    }
                }
                if(success)break;
                len--;
                
            }
            cout << ss[0].substr(ss[0].size() - len) << endl;
        }
        
        return 0;
    }
    

    从最大的长度开始枚举,最大的后缀长度就是全部字符串中最短的字符串.
    那么找最大长度的时候可以在输入的时候就找出最大值.
    这种找出最长公共前缀,最长公共后缀的题目都是一样的,都是枚举比较,都是从第二个开始比较,因为都是和第一个进行比较

  • 相关阅读:
    GitLab基本用法
    SSH免密登录详解
    一文搞懂GitLab安装部署及服务配置
    初识:LevelDB
    Jenkins安装与Gitlab项目部署详解
    CentOS7的安装和配置
    C/C++语言的学习方向
    C语言atoi函数
    C语言整数的取值范围
    C语言scanf函数
  • 原文地址:https://www.cnblogs.com/Rowry/p/13922900.html
Copyright © 2011-2022 走看看