校招字符串相关高频算法题汇总【C++实现】-1

简介: 校招字符串相关高频算法题汇总【C++实现】

1、反转字符串

首先我们来看第一道,先从简单一点的开始做起✍

① 题目描述:

力扣原题

image.png

class Solution {
public:
    void reverseString(vector<char>& s) {
    }
};

② 思路分析:

  • 本题很简单,就是将题目中给出的字符串做一个前后逆置的操作。这边首先想到的就是双指针的一个思路,让一个指针i在前,一个指针j在后,相对而行,不断交互二者位置上的字符,直到二者相遇为止

image.png

③ 代码展示:

  • 代码很简洁,一个for循环就能搞定了
void reverseString(vector<char>& s) {
    for(int i = 0, j = s.size() - 1; i < j; ++i, --j)
    {
        swap(s[i], s[j]);
    }
}

④ 运行结果:

  • 来看看执行结果,发现效率中一般,不过能AC就行😁

image.png

2、反转字符串||

接下去再来看第二道,稍稍复杂一些

① 题目描述:

力扣原题

image.png

class Solution {
public:
    string reverseStr(string s, int k) {
    }
};

② 思路分析:

  • 本题相对上一题来说就发生了一些变化,虽然都是在反转字符串,但本题呢不是一个一个地在遍历,而是2k个2k个地在遍历,在题目给到的参数中还有一个k,我们在遍历这个字符串时是 ==一次遍历2k个,然后反转前k个==
  • 在不断执行的过程中总会碰到结束,此时题目又给出了两种结果。
  1. 也就是当剩余的字符少于2k个,但是大于等于k个的话,则反转前k个,这个其实我们可以和题干中的意思做一个结合,多加一个是否到达结尾即可
  2. 当剩余的字符连k个都不足的话,此时将剩余的全部反转即可
  • 很多同学在写本题的时候都会纠结于这个2k,所以在循环遍历的时候会选择拿一个计数器去计数,然后当这个计数器到达的 2k 的时候就对前k个去做反转,其实没必要这样,我们在这里直接去修改循环遍历的次数即可,即i += 2 * k,让i每次移动的距离就是 2k 个,那当我们在遍历的时候也无需去考虑那么多了

接下去呢我们就要在循环内部去反转对应的字符串了,这里大家可以自己实现一个(就是我们上一题所讲的代码),或者是直接使用库函数【reverse】

  • 不过记住了在库函数这里我们要传递的是 ==迭代器==,注意这里【reverse】是左闭右开的,所以不包含i + k这个位置
reverse(s.begin() + i, s.begin() + i + k);
  • 不过呢也不是任何区间我们都可以去做反转的,至少不能发生越界的情况,此时我们给出判断的条件i + k <= s.size(),因为i + k这个位置是不包含的,所以我们在判断条件中要加上
  • 那在反转之后为什么还要再加一个continue呢,原因就在于我们只是反转前k个,而后k个是不动的,所以在反转完后直接继续向后比较遍历 2k 个即可
if(i + k <= s.size())
{
    reverse(s.begin() + i, s.begin() + i + k);  
    continue;       // 只翻转前k个, 翻转完后继续遍历               
}
  • 那在之后呢我们还要去考虑到这个剩余的字符问题,小于2k但是大于等于k已经包含在了上面的代码中,而我们此时要考虑的就是不足k个的问题,所以直接反转从当前的位置到结尾即可
// 考虑到最后的字符少于k个的情况
reverse(s.begin() + i, s.end());

③ 代码展示:

  • 最后来看一下整体的代码,可以看出也不是非常复杂
string reverseStr(string s, int k) {
    for(int i = 0;i < s.size(); i += 2 * k)
    {
        if(i + k <= s.size())
        {
            reverse(s.begin() + i, s.begin() + i + k);  
            continue;       // 只翻转前k个, 翻转完后继续遍历               
        }
        // 考虑到最后的字符少于k个的情况
        reverse(s.begin() + i, s.end());
    }
    return s;
}

④ 运行结果:

  • 最后再来看下执行结果吧

image.png

3、字符串最后一个单词的长度

然后是第三题,我们来看看和 string类 API相关的一些题目

① 题目描述:

牛客原题

image.png

② 思路分析:

  • 好,我们来分析一下本题该如何去进行求解。题目的意思很明确,就是给你一个字符串,然后计算出这个字符串最后一个单词的长度
  • 我们知道单词都是以【空格】作为分割,此时我们只需要去找到最后一个空间即可,然后计算出从这个空格开始到结尾有多少字符

此时我们可以使用到的是string类rfind() 接口,从一个字符串开始从后往前进行寻找,找第一个空格

size_t pos = s.rfind(' ');
  • 那如果找到了这个空格的话就可以去输出最后一个单词的长度的,因为这是【左闭右开】的,而我们不能计算上pos这个位置空格的长度,所以要从pos + 1的位置开始计数
cout << s.size() - (pos + 1) << endl;
  • 不过呢我们还要考虑到给到的字符串没有空格的情况,这个时候返回这个字符串的【size】即可
// 如果不存在空格, 直接返回当前字符串的长度
cout << s.size() << endl;
  • 这里我们先执行一下试试看,发现取到的长度是5,这是为什么呢?

image.png

  • 面对这个问题而言我们可以到VS上去调试一下,发现当使用cin >>流插入在进行读取的时候只读到了前面的【hello】,但是呢后面的【newcoder】却没有读取到,这个我们在讲解 STL中的string类 时就有说到过,对于像cin >>scanf()这些都是会去缓冲区里面读内容,但是呢在读取到空格的时候就会自动截止了,而无法完成整行的读取

image.png

  • 而我们若是要进行一整行读取的话可以使用get(),不过在这里呢我更推荐 getline() 函数,传递进流对象cin和所要读取的string对象,就可以去读取到整行的信息

image.png

  • 然后再去读取的时候就发现确实读到了这一整行

image.png

③ 代码展示:

  • 代码如下,具体见运行结果
#include <iostream>
#include <string>
using namespace std;
int main() {
    string s;
    getline(cin, s);
    //cin >> s;
    size_t pos = s.rfind(' ');
    if (pos) {
        cout << s.size() - (pos + 1) << endl;
    } 
    else {
        // 如果不存在空格, 直接返回当前字符串的长度
        cout << s.size() << endl;
    }
}

④ 运行结果:

image.png

4、找字符串中第一个只出现一次的字符

第四题的话我们再来看看与其他数据结构相结合的题目

① 题目描述:

力扣原题

image.png

② 思路分析:

  • 本题的题意很清晰,我们要去找的就是在一个字符串中第一次出现的,也是唯一一次出现的字符
  • 那么也就意味着我们需要去每一个字符所出现的次数,这立马让我想到了【哈希表】,不知读者一看到此题是什么反应呢?

首先我们考虑先去定义一个哈希表出来,使用到的是unordered_map,【key】的类型是char、【value】的类型是int

unordered_map<char, int> count;
  • 接下去我们就遍历这个字符串s然后统计出每一个字符所出现的次数即可
for(char c: s)
{
    count[c]++;     // 统计每个字符出现的次数,放入哈希表中存起来
}
  • 最后一步,我们再去遍历这个字符串,然后根据对应的字符到哈希表中去寻找即可,一旦找到一个字符所对应的【value】值是1的话,那就返回这个字符在字符串中所在下标。若是找了一圈还没有发现为1的话,则表示在字符串中并没有只出现一次的数字
// 遍历该统计数组,若是找到第一个只出现一次的,返回其坐标
for(int i = 0;i < s.size(); ++i)
{
    if(count[s[i]] == 1)
        return i;
}

③ 代码展示:

int firstUniqChar(string s) {
    unordered_map<char, int> count;
    for(char c: s)
    {
        count[c]++;     //统计每个字符出现的次数,放入哈希表中存起来
    }
    //遍历该统计数组,若是找到第一个只出现一次的,返回其坐标
    for(int i = 0;i < s.size(); ++i)
    {
        if(count[s[i]] == 1)
            return i;
    }
    return -1;
}

④ 运行结果:

image.png


5、仅仅反转字母

接下去我们进阶地要来做一做反转相关的题目

① 题目描述:

力扣原题

image.png

② 思路分析:

  • 首先来解读一下本题的意思,和第一小题反转字符串很类似,但是呢它在反转的时候不是全部的字符都会反转,而是只反转那些 大小写的字母,除此之外都会别忽略
  • 那首先我们双指针的做法还是可行的,只是在从两头往中间遍历的时候需要去判断一下当前字符是不是大小写字母。库里虽然是有API给我们调用,但是呢最好自己写一下,这样比较好控制
bool IsUpperOrLowerLetter(char ch)
{
    if((ch >= 'a' && ch <= 'z')
    || (ch >= 'A' && ch <= 'Z'))
        return true;
    return false;
}
  • 下面我画了一个算法分解图,以题目中的示例3为例

image.png

  • 上面这个交换的过程中最重要的逻辑就是下面的和这个对当前所在字符的判断,如果当前这个字符不是大小写中英文的话,就继续 前移或者后移
while(begin < end && !IsUpperOrLowerLetter(s[begin]))
    begin++;
while(begin < end && !IsUpperOrLowerLetter(s[end]))
    end--;   

③ 代码展示:

  • 好,我们展示一下整体代码
bool IsUpperOrLowerLetter(char ch)
{
    if((ch >= 'a' && ch <= 'z')
    || (ch >= 'A' && ch <= 'Z'))
        return true;
    return false;
}
string reverseOnlyLetters(string s) {
    int begin = 0, end = s.size() - 1;
    while(begin < end)
    {
        while(begin < end && !IsUpperOrLowerLetter(s[begin]))
            begin++;
        while(begin < end && !IsUpperOrLowerLetter(s[end]))
            end--;        
        swap(s[begin++], s[end--]);        
    }
    return s;
}

④ 运行结果:

  • 来看看执行结果,发现还不错哦!

image.png

6、验证一个字符串是否是回文

然后我们再来试试验证回文串,本题和上面一题部分类似,可做借鉴

① 题目描述:

力扣原题

image.png

② 思路分析:

  • 首先来解释一下为什么本题可以借鉴上一题,原因就在于在进行某些判断的时候都需要去忽略一些除指定字符以外的其他字符,而且在遍历的时候也是采取双指针的做法,
  • 下面就是对于当前字符是否为字母或者数字的一个判断
bool IsLetterOrDigit(char ch)
{
    if((ch >= 'a' && ch <= 'z')
    || (ch >= 'A' && ch <= 'Z')
    || (ch >= '0' && ch <= '9'))
        return true;
    return false;
}

然后我们来从头分析一下

  • 首先的话题目给出了要求,我们在对这些字符进行判断的时候首先要将它们都转换为小写,可以看到我这里使用到了C++11的一个新语法【范围for】,而且对字符串中的每一个字符都做了引用,这样就可以使得内部的修改带动外部
// 1.将字符串中的所有字符转换为小写
for(auto& c: s)
{
    if(c >= 'A' && c <= 'Z')
        c += 32;
}
  • 接下去呢我们就可以去做遍历判断了,这里也给出关键的代码,如果你认真看过上题的话就可以知道,它们的思路基本都是一致的:一个指针从前往后,一个指针从后往前,若是相等的话则继续遍历,直到遇到二者不同为止则return false。如果在遍历结束了之后没发现不同的话这就是个回文串
// 通过循环略过非字母或 数字的字符
while(begin < end && !IsLetterOrDigit(s[begin])){
    begin++;
}
while(begin < end && !IsLetterOrDigit(s[end])){
    end--;
}     
  • 还有一点可以在开头就去做一个判断,当着字符串中没有任何字符而是一个空串的时候,此时它一定是一个回文串,直接return true即可
if(s.size() == 0)
    return true;

③ 代码展示:

  • 本题的代码较上面的题目还是慢慢多了一些,读者可以先好好看看
bool IsLetterOrDigit(char ch)
{
    if((ch >= 'a' && ch <= 'z')
    || (ch >= 'A' && ch <= 'Z')
    || (ch >= '0' && ch <= '9'))
        return true;
    return false;
}
bool isPalindrome(string s) {
    if(s.size() == 0)
        return true;
    // 1.将字符串中的所有字符转换为小写
    for(auto& c: s)
    {
        if(c >= 'A' && c <= 'Z')
            c += 32;
    }
    // 2.前后指针遍历判断
    int begin = 0, end = s.size() - 1;
    while(begin < end)
    {
        // 通过循环略过非字母或 数字的字符
        while(begin < end && !IsLetterOrDigit(s[begin])){
            begin++;
        }
        while(begin < end && !IsLetterOrDigit(s[end])){
            end--;
        }       
        if(s[begin] != s[end])
        {
            return false;
        }else{
            begin++;
            end--;
        }
    }
    return true;
}

④ 运行结果:

image.png

相关文章
|
25天前
|
算法 C++ 容器
C++标准库中copy算法的使用
C++标准库中copy算法的使用
13 1
|
24天前
|
算法
【算法】滑动窗口——找到字符串中所有字母异位词
【算法】滑动窗口——找到字符串中所有字母异位词
|
22天前
|
算法 Java
掌握算法学习之字符串经典用法
文章总结了字符串在算法领域的经典用法,特别是通过双指针法来实现字符串的反转操作,并提供了LeetCode上相关题目的Java代码实现,强调了掌握这些技巧对于提升算法思维的重要性。
|
3天前
|
存储 C++
C++(五)String 字符串类
本文档详细介绍了C++中的`string`类,包括定义、初始化、字符串比较及数值与字符串之间的转换方法。`string`类简化了字符串处理,提供了丰富的功能如字符串查找、比较、拼接和替换等。文档通过示例代码展示了如何使用这些功能,并介绍了如何将数值转换为字符串以及反之亦然的方法。此外,还展示了如何使用`string`数组存储和遍历多个字符串。
|
18天前
|
算法 搜索推荐 C++
c++常见算法
C++中几种常见算法的示例代码,包括查找数组中的最大值、数组倒置以及冒泡排序算法。
13 0
|
2月前
|
自然语言处理 算法 搜索推荐
字符串相似度算法完全指南:编辑、令牌与序列三类算法的全面解析与深入分析
在自然语言处理领域,人们经常需要比较字符串,这些字符串可能是单词、句子、段落甚至是整个文档。如何快速判断两个单词或句子是否相似,或者相似度是好还是差。这类似于我们使用手机打错一个词,但手机会建议正确的词来修正它,那么这种如何判断字符串相似度呢?本文将详细介绍这个问题。
223 1
|
25天前
|
算法 C++ 容器
【C++算法】双指针
【C++算法】双指针
|
1月前
|
算法 C++
惊爆!KPM算法背后的秘密武器:一行代码揭秘字符串最小周期的终极奥义,让你秒变编程界周期大师!
【8月更文挑战第4天】字符串最小周期问题旨在找出字符串中最短重复子串的长度。KPM(实为KMP,Knuth-Morris-Pratt)算法,虽主要用于字符串匹配,但其生成的前缀函数(next数组)也可用于求解最小周期。核心思想是构建LPS数组,记录模式串中每个位置的最长相等前后缀长度。对于长度为n的字符串S,其最小周期T可通过公式ans = n - LPS[n-1]求得。通过分析周期字符串的特性,可证明该方法的有效性。提供的C++示例代码展示了如何计算给定字符串的最小周期,体现了KPM算法在解决此类问题上的高效性。
44 0
|
2月前
|
算法 C++
2730. 找到最长的半重复子字符串(c++,滑动窗口)
2730. 找到最长的半重复子字符串(c++,滑动窗口)
|
2月前
|
C++
567. 字符串的排列(c++)滑动窗口
567. 字符串的排列(c++)滑动窗口
下一篇
DDNS