【九】【C++】string练习

917. 仅仅反转字母

给你一个字符串 s ,根据下述规则反转字符串:

  • 所有非英文字母保留在原有位置。

  • 所有英文字母(小写或大写)位置反转。

返回反转后的 s

示例 1:

输入:s = "ab-cd" 输出:"dc-ba"

示例 2:

输入:s = "a-bC-dEf-ghIj" 输出:"j-Ih-gfE-dCba"

示例 3:

输入:s = "Test1ng-Leet=code-Q!" 输出:"Qedo1ct-eeLg=ntse-T!"

提示

  • 1 <= s.length <= 100

  • s 仅由 ASCII 值在范围 [33, 122] 的字符组成

  • s 不含 '\"''\\'

 
  
class Solution {
public:
    bool isLeeter(char ch) {
        if (ch >= 'a' && ch <= 'z') {
            return true;
        }
        if (ch >= 'A' && ch <= 'Z') {
            return true;
        }

        return false;
    }
    string reverseOnlyLetters(string s) {
        
        size_t begin = 0, end = s.size() - 1;
        while (begin < end) {
            while (begin < end && !isLeeter(s[begin]))
                begin++;
            while (begin < end && !isLeeter(s[end]))
                end--;
            swap(s[begin], s[end]);
            begin++;
            end--;
        }
        return s;
    }
};

isLeeter 函数

isLeeter是一个辅助函数,用于判断给定的字符ch是否为字母(大写或小写)。它通过检查ch是否位于'a''z''A''Z'的范围内来实现这一点。如果ch是字母,函数返回true;否则返回false

reverseOnlyLetters 函数

reverseOnlyLetters函数接受一个字符串s作为参数,返回一个新字符串,其中s中的所有字母都被反转,而非字母字符保持原位。

这个函数使用了两个指针(或索引)beginend,分别指向字符串的开始和结束。然后,它进入一个循环,其中:

首先,它使用isLeeter函数检查begin指向的字符是否不是字母,如果是,begin就向右移动,直到它指向一个字母或超过end

类似地,它检查end指向的字符,如果不是字母,end就向左移动,直到它指向一个字母或小于begin

一旦beginend都指向字母,就交换这两个位置的字符,并将begin向右移动一位,将end向左移动一位。

这个过程一直进行,直到begin大于或等于end为止,这意味着所有的字母都已经检查并在需要时进行了反转。

387. 字符串中的第一个唯一字符

给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1

示例 1:

输入: s = "leetcode" 输出: 0

示例 2:

输入: s = "loveleetcode" 输出: 2

示例 3:

输入: s = "aabb" 输出: -1

提示:

  • 1 <= s.length <= 10(5)

  • s 只包含小写字母

 
  
class Solution {
public:
    int firstUniqChar(string s) {
    int Count[26];
    for(auto x:s){
        Count[x-'a']++;
    }
    for(int i=0;i

定义字符计数数组

int Count[26];这行代码定义了一个整型数组Count,大小为26,用于存储字符串s中每个字母出现的次数。数组的每个元素对应英文字母表中的一个字母,即Count[0]对应字母'a'的出现次数,Count[25]对应字母'z'的出现次数。注意,这里假设字符串s只包含小写英文字母。

计数循环

for(auto x:s){ Count[x-'a']++; }这个循环遍历字符串s中的每个字符x。对于每个字符,它通过x-'a'计算出字符x相对于'a'的偏移量,这个偏移量即为数组Count中对应的索引。然后,它通过Count[x-'a']++递增这个索引处的值,从而计算出每个字母在字符串中出现的次数。

检查第一个唯一字符

for(int i=0;i这个循环遍历字符串s,从头开始检查每个字符。对于每个字符s[i],它同样计算出相对于'a'的偏移量,并在Count数组中查找这个偏移量对应的出现次数。如果某个字符的出现次数等于1(Count[s[i]-'a']==1),这意味着这个字符是唯一的(不重复的),函数立即返回这个字符的索引i

没有找到唯一字符

如果函数能够完成上述循环而没有返回任何索引,这意味着字符串中没有不重复的字符。因此,函数最后返回-1,表示没有找到不重复的字符。

时间复杂度与空间复杂度

这种方法的空间复杂度为O(1)(因为Count数组的大小是固定的),时间复杂度为O(n),其中n是字符串的长度。

字符串最后一个单词的长度_牛客题霸_牛客网

描述

计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)

输入描述:

输入一行,代表要计算的字符串,非空,长度小于5000。

输出描述:

输出一个整数,表示输入字符串最后一个单词的长度。

示例1

输入:

hello nowcoder

输出:

8

说明:

最后一个单词为nowcoder,长度为8

 
  
#include 

#include 
using namespace std;

int main() {
    string s;
    getline(cin, s);
    int pos=s.rfind(' ');
    string sub=s.substr(pos+1);
    cout<

getlinecin的区别

getlinecin 都是C++中用于从标准输入读取用户输入的方法,但它们有一些重要的区别:

数据类型:

  cin 通常用于从标准输入读取基本数据类型,如整数、浮点数、字符等。

  getline 主要用于读取文本行,通常用于读取字符串。

分隔符:

  cin 通过空格、制表符或换行符来分隔输入,它会在空白字符前停止读取。

  getline 会读取整行文本,包括换行符,直到遇到换行符为止。因此,它可以读取包含空格的完整文本行。

处理空格:

  cin 遇到空格时会停止读取,因此无法读取包含空格的字符串。

  getline 能够处理包含空格的文本,因为它会一直读取到换行符为止。

 
  
#include 
#include 

int main() {
    std::string line;
    std::cout << "Enter a line of text: ";
    std::getline(std::cin, line);
    std::cout << "You entered: " << line << std::endl;
    return 0;
}

125. 验证回文串

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false

示例 1:

输入: s = "A man, a plan, a canal: Panama" 输出:true 解释:"amanaplanacanalpanama" 是回文串。

示例 2:

输入:s = "race a car" 输出:false 解释:"raceacar" 不是回文串。

示例 3:

输入:s = " " 输出:true 解释:在移除非字母数字字符之后,s 是一个空字符串 "" 。 由于空字符串正着反着读都一样,所以是回文串。

提示:

  • 1 <= s.length <= 2 * 10(5)

  • s 仅由可打印的 ASCII 字符组成

 
  
class Solution {
public:
    bool isLeeterOrNumber(char ch) {
        return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
               (ch >= '0' && ch <= '9');
    }
    bool isPalindrome(string s) {
        for (auto& x : s)
            if (x >= 'A' && x <= 'Z')
                x = x + 32;

        int begin = 0, end = s.size() - 1;
        while (begin < end) {
            while (begin < end && !isLeeterOrNumber(s[begin])) {
                begin++;
            }
            while (begin < end && !isLeeterOrNumber(s[end])) {
                end--;
            }
            if (s[begin] != s[end])
                return false;
            begin++;
            end--;
        }
        return true;
    }
};

isLeeterOrNumber 函数:

  这是一个辅助函数,用于检查给定字符是否是字母(大小写)或数字。

  函数返回 true 如果字符是字母(小写或大写)或数字,否则返回 false

isPalindrome 函数:

  这个函数接受一个字符串 s 作为输入参数,用于判断该字符串是否是回文串。

  在函数内部,首先将字符串中的大写字母转换为小写字母,这是为了使字符串中的字母不区分大小写。

  然后,使用两个指针 beginend 分别指向字符串的开头和结尾。

  进入一个循环,该循环会在 begin 小于 end 时执行,因为我们只需要比较一半的字符。

  在循环中,首先通过两个内部循环移动 beginend 指针,直到它们指向字母或数字。这是为了跳过非字母和非数字的字符。

  一旦两个指针指向字母或数字,它们会比较指向的字符是否相同。如果不相同,函数会立即返回 false,表示字符串不是回文串。

  如果相同,begin 指针向后移动一步,end 指针向前移动一步,然后继续比较下一对字符。

  如果在整个循环过程中都没有发现不同的字符,那么字符串被认为是回文串,函数返回 true

415. 字符串相加

给定两个字符串形式的非负整数 num1num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例 1:

输入:num1 = "11", num2 = "123" 输出:"134"

示例 2:

输入:num1 = "456", num2 = "77" 输出:"533"

示例 3:

输入:num1 = "0", num2 = "0" 输出:"0"

提示:

  • 1 <= num1.length, num2.length <= 10(4)

  • num1num2 都只包含数字 0-9

  • num1num2 都不包含任何前导零

 
  
class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1 = num1.size() - 1;
        int end2 = num2.size() - 1;
        int value1 = 0, value2 = 0, next = 0;
        string addret;
        while (end1 >= 0 || end2 >= 0) {
            if (end1 >= 0) {
                value1 = num1[end1--] - '0';
            } else {
                value1 = 0;
            }
            if (end2 >= 0) {
                value2 = num2[end2--] - '0';
            } else {
                value2 = 0;
            }

            int valueRet = value1 + value2 + next;
            if (valueRet > 9) {
                next = 1;
                valueRet -= 10;
            } else {
                next = 0;
            }
            addret += valueRet + '0';
        }
        if (next == 1) {
            addret += '1';
        }
        reverse(addret.begin(), addret.end());
        return addret;
    }
};

addStrings 函数:

  这个函数接受两个字符串 num1num2,这两个字符串表示要相加的非负整数。

  函数会从字符串的末尾开始逐位相加,因为整数的低位在字符串的末尾。

  end1end2 分别初始化为两个输入字符串的末尾索引。

  value1value2 分别用于存储当前位置的两个字符所代表的数字。

  next 用于存储进位,初始化为0。

  addret 是一个空字符串,用于存储最终的相加结果。

进行循环迭代:

  使用 while 循环,直到 end1end2 都小于0,表示两个字符串的所有位都已相加完毕。

  在循环中,首先获取 end1end2 位置的字符,将其减去字符 '0' 得到对应的数字值。

  然后将这两个数字值和进位 next 相加,得到 valueRet

  如果 valueRet 大于9,表示需要进位,将 next 设置为1,并且 valueRet 减去10,确保结果不大于9。

  将 valueRet 转换为字符,并将其添加到 addret 中,以构建相加的结果字符串。

  继续下一位的相加。

处理最终进位:

  循环结束后,如果最高位相加产生了进位,需要将额外的1添加到结果字符串的末尾。

最后,将 addret 字符串反转,以得到正确的顺序(因为之前是从末尾开始相加的)。

返回结果字符串 addret,它包含了两个输入字符串相加的结果。

541. 反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。

  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例 1:

输入:s = "abcdefg", k = 2 输出:"bacdfeg"

示例 2:

输入:s = "abcd", k = 2 输出:"bacd"

提示:

  • 1 <= s.length <= 10(4)

  • s 仅由小写英文组成

  • 1 <= k <= 10(4)

 
  
class Solution {
public:
    void reverse(string& s, int begin, int k) {
        int begin1 = begin;
        int end1 = begin + k - 1;
        while (begin1 < end1) {
            swap(s[begin1], s[end1]);
            begin1++;
            end1--;
        }
    }
    string reverseStr(string s, int k) {
        int count = 0;
        int begin = 0;
        int end = 0;

        while (end < s.size()) {
            if (count == 2 * k) {
                reverse(s, begin, k);
                begin = end;
                count = 0;
            } else {
                end++;
                count++;
            }
        }

        if (end - begin < k) {
            reverse(s, begin, end - begin);
        } else {
            reverse(s, begin, k);
        }
        return s;
    }
};

方法 void reverse(string& s, int begin, int k)

这个方法用于反转字符串s中从begin位置开始的k个字符。

begin1end1分别初始化为段的开始位置和结束位置(begin + k - 1)。

通过一个while循环,使用swap函数交换begin1end1位置上的字符,然后begin1向后移动一位,end1向前移动一位,直到begin1大于等于end1,完成反转操作。

方法 string reverseStr(string s, int k)

count用于跟踪当前已经处理的字符数。

beginend-1分别表示当前处理段的开始和结束位置。

通过一个while循环遍历整个字符串:

  如果count达到2k,说明当前段已经处理完毕,调用reverse方法反转这段的前k个字符,然后更新beginend的位置,重置count为0,以处理下一段。

  否则,end向后移动一位,count加1,继续遍历。

循环结束后,根据end - begin的值判断剩余字符的数量:

  如果少于k个,反转从begin开始到字符串末尾的所有字符。

  如果在k2k个之间,只反转前k个字符。

最后返回修改后的字符串s

557. 反转字符串中的单词 III

给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

示例 1:

输入:s = "Let's take LeetCode contest" 输出:"s'teL ekat edoCteeL tsetnoc"

示例 2:

输入: s = "Mr Ding" 输出:"rM gniD"

提示:

  • 1 <= s.length <= 5 * 10(4)

  • s 包含可打印的 ASCII 字符。

  • s 不包含任何开头或结尾空格。

  • s至少 有一个词。

  • s 中的所有单词都用一个空格隔开。

 
  

class Solution {
public:
    void reverse(string& s, int begin, int end) {
        while (begin < end) {
            swap(s[begin], s[end]);
            begin++;
            end--;
        }
    }
    string reverseWords(string s) {
        int begin = 0;
        int end = 0;

        while (begin < s.size()) {
            int pos = s.find(' ', begin);
            if (pos != std::string::npos) {
                end = pos - 1;
            } else {
                end = s.size() - 1;
            }
            reverse(s, begin, end);
            begin = end + 2;
        }
        return s;
    }
};

方法 void reverse(string& s, int begin, int end)

这个辅助方法用于反转字符串s中从begin位置到end位置的子字符串。

通过一个while循环,当begin小于end时,使用swap函数交换两个位置上的字符,然后begin自增,end自减,直到begin大于或等于end,完成子字符串的反转。

方法 string reverseWords(string s)

这个方法用于反转字符串s中的每个单词。

初始化两个整数beginend,分别表示当前处理的单词的开始和结束位置。

使用一个while循环遍历整个字符串:

  使用std::string::find方法从当前begin位置开始查找下一个空格的位置,这个位置标记了当前单词的结束。

  如果找到了空格(pos不等于std::string::npos),则设置endpos - 1,即当前单词最后一个字符的位置。

  如果没有找到空格,说明已经到达字符串的末尾,此时设置ends.size() - 1

  调用reverse方法反转从beginend的子字符串,即反转当前单词。

  更新beginend + 2,即下一个单词的开始位置。这里加2是因为end是当前单词最后一个字符的位置,所以end + 1是空格的位置,再加1才是下一个单词的开始位置。

循环继续直到begin大于或等于字符串的大小,此时所有单词都已被反转。

函数最后返回修改后的字符串s

43. 字符串相乘

给定两个以字符串形式表示的非负整数 num1num2,返回 num1num2 的乘积,它们的乘积也表示为字符串形式。

注意:不能使用任何内置的 BigInteger 库或直接将输入转换为整数。

示例 1:

输入: num1 = "2", num2 = "3" 输出: "6"

示例 2:

输入: num1 = "123", num2 = "456" 输出: "56088"

提示:

  • 1 <= num1.length, num2.length <= 200

  • num1num2 只能由数字组成。

  • num1num2 都不包含任何前导零,除了数字0本身。

num1="123",num2="456",num1*num2等价于123*456,等价于123*(400+50+6),等价于123*4*100+123*5*10+123*1*1。

class Solution {
public:
    string multiply(string a, string b) {
        //我们知道乘法问题其实就是加法问题
        //这道题我们模拟一下过程就行

        //我们用长的作为乘数,与被乘数的每一位相乘,需要写一个乘法函数
        //我们需要用vector将每次乘的结果保存起来
        //将vector中的每个串进行相应的移位操作,也就是后面补零即可
        //写一个两两相加函数

        //优化:滚动数组

        
        //如果有一个为零,返回0即可
        if(a == "0" || b == "0") return "0";
        int m = a.size(), n = b.size();
        
        if(m < n) {
            return multiply(b, a);
        }
        //处理了之后我们默认a是长的,即乘数

        vector vec(n, "");

        for(int i = n - 1; i >= 0; i --) {
            vec[i] = getMultiply(a, b[i]); //相乘
            oper(vec[i], n - (i + 1)); //补零
        }

        string ans = "";
        for(int i = 0; i < n; i ++) {
            ans = add(ans, vec[i]);
        }

        return ans;

    }

    string getMultiply(string& a, char c) {
        int x = c - '0';
        string ans = "";
        int carry = 0;
        for(int i = a.size() - 1; i >= 0; i --) {
            int sum = (a[i] - '0') * x + carry;
            ans += sum % 10 + '0';
            carry = sum / 10;
        }

        if(carry) ans += carry + '0';
        reverse(ans.begin(), ans.end());
        return ans;
    }

    void oper(string& a, int n) {
        while(n --) {
            a += '0';
        }
    }

    string add(string& a, string& b) {
        //逆序求和,跟当时链表高精度一样的思路

        string ans = "";
        // reverse(a);
        // reverse(b);

        int n = a.size() - 1, m = b.size() - 1;
        int carry = 0;
        // int idx = 0;
        while(n >= 0 || m >= 0) {
            int x = n < 0 ? 0 : a[n] - '0';
            int y = m < 0 ? 0 : b[m] - '0';
            int sum = x + y + carry;

            ans += (sum % 10) + '0';

            carry = sum / 10;

            n --;
            m --;
        }

        if(carry) ans += '1';
        // reverse(ans);
        reverse(ans.begin(), ans.end());
        
        return ans;
    }
};

边界情况处理

如果任一乘数为"0",则直接返回"0"。

确保乘数a是较长的字符串

如果a的长度小于b的长度,则调换ab,确保a是较长的字符串。这样做是为了简化后续处理过程。

逐位乘法

使用vector vec(n, "")来存储每一步的乘法结果。这里n是乘数b的长度。

对于乘数b中的每一位字符b[i],调用getMultiply(a, b[i])计算它与a的乘积,并将结果存储在vec[i]中。

补零操作

对于每个乘积结果vec[i],调用oper(vec[i], n - (i + 1))来补零,模拟乘法中的位移操作。

累加所有乘积结果

初始化一个空字符串ans作为累加结果。

遍历vec中的每个字符串,使用add(ans, vec[i])将其累加到ans中。

辅助函数

getMultiply(string& a, char c):计算字符串a与单个字符c的乘积,处理进位,并返回结果字符串。

oper(string& a, int n):在字符串a的末尾补n个零,模拟乘法的位移操作。

add(string& a, string& b):将两个字符串表示的数值相加,并返回结果字符串。这个函数从字符串的末尾开始逐位相加,处理进位。

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

你可能感兴趣的:(C++,c++,leetcode,算法)