【C++经典例题】回文串判断:两种高效解法剖析

💓 博客主页:倔强的石头的CSDN主页****

📝Gitee主页:倔强的石头的gitee主页****

⏩ 文章专栏:C++经典例题

期待您的关注

目录

一、问题描述

示例

[二、解法一:将字母数字连接到新的 string](#二、解法一:将字母数字连接到新的 string)

思路

代码实现

代码解释

复杂度分析

三、解法二:直接原地筛选判断

思路

代码实现

代码解释

复杂度分析

总结


一、问题描述

在字符串处理中,判断一个字符串是否为回文串是一个经典问题。

本题有特殊要求:在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,如果短语正着读和反着读都一样,则认为该短语是一个回文串。字母和数字都属于字母数字字符。

示例

  • 输入: s = "A man, a plan, a canal: Panama",输出:true,解释:处理后得到 "amanaplanacanalpanama" 是回文串。
  • 输入:s = "race a car",输出:false,解释:处理后得到 "raceacar" 不是回文串。
  • 输入:s = " ",输出:true,解释:移除非字母数字字符后,s 是一个空字符串 "",空字符串正着反着读都一样,所以是回文串。

原题链接

125. 验证回文串 - 力扣(LeetCode)

二、解法一:将字母数字连接到新的 string

思路

这种方法的核心思想是先遍历原字符串,把其中的字母数字字符提取出来,同时将大写字母转换为小写字母,存储到一个新的字符串 tmp

然后再对新字符串 tmp 进行回文判断

代码实现

cpp 复制代码
#include <iostream>
#include <string>

class Solution {
public:
    bool isPalindrome(string s) //第一种方式 将字母数字连接到新的string
    {
        string tmp;
        string::iterator left = s.begin();
        string::iterator right = s.end();

        while (left != right)//第一次遍历,所有字母数字转入新string,并且统一为小写
        {
            if ((*left >= '0' && *left <= '9') || (*left >= 'a' && *left <= 'z'))
                tmp += *left;
            if (*left >= 'A' && *left <= 'Z')
                tmp += (*left + 32);
            ++left;
        }

        if (tmp.empty())//如果新string为空,则判定为回文串
            return true;

        left = tmp.begin();
        right = tmp.end() - 1;
        while (left <= right)//第二次遍历 左右迭代器逐个对比
        {
            if (*left == *right)
            {
                ++left;
                --right;
            }
            else
                return false;
        }
        return true;
    }
};

代码解释

  1. 提取字母数字并转换大小写
    • 定义一个空字符串 tmp 用于存储处理后的字符。
    • 使用迭代器 left 遍历原字符串 s,当遇到数字('0''9')或小写字母('a''z')时,直接添加到 tmp 中;当遇到大写字母('A''Z')时,将其 ASCII 码值加 32 转换为小写字母后添加到 tmp 中。
  2. 处理空字符串情况 :如果 tmp 为空字符串,根据题目定义,空字符串是回文串,直接返回 true
  3. 回文判断 :使用双指针法,left 指向 tmp 的开头,right 指向 tmp 的结尾,逐个比较对应位置的字符。如果不相等,返回 false;如果都相等,最终返回 true

复杂度分析

  • 时间复杂度:O(n)需要遍历原字符串一次,再遍历新字符串一次。
  • 空间复杂度:O (n),其中 n是处理后新字符串的长度。

三、解法二:直接原地筛选判断

思路

这种方法不创建新的字符串,而是直接在原字符串上使用双指针法进行筛选和判断。通过两个指针 leftright 分别从字符串的两端开始向中间移动,跳过非字母数字字符,同时将字符转换为小写后进行比较。

代码实现

cpp 复制代码
#include <iostream>
#include <string>
#include <cctype>

class Solution {
public:
    bool isPalindrome(std::string s) //第二种方式,直接原地筛选判断
    {
        string::iterator left = s.begin();
        string::iterator right = s.end() - 1;

        while (left < right)
        {
            // 跳过左边的非字母数字字符
            while (left < right && !isalnum(*left))
            {
                ++left;
            }
            // 跳过右边的非字母数字字符
            while (left < right && !isalnum(*right))
            {
                --right;
            }

            if (left < right) {
                // 将字符转换为小写后比较
                if (tolower(*left) != tolower(*right))
                {
                    return false;
                }
                ++left;
                --right;
            }
        }
        //跳出循环,要么left==right,要么left>right 
        //说明所有需要比较的字符对都已经检查过,且都相等
        return true;
    }
};

代码解释

  1. 初始化指针 :使用迭代器 left 指向字符串的开头,right 指向字符串的结尾。
  2. 跳过非字母数字字符 :通过两个内层 while 循环,分别将 leftright 指针移动到字母数字字符的位置。
  3. 字符比较 :将 leftright 指针指向的字符转换为小写后进行比较,如果不相等,返回 false;如果相等,继续移动指针。
  4. 返回结果 :当 left 大于等于 right 时,说明所有需要比较的字符对都已经检查过,且都相等,返回 true

复杂度分析

  • 时间复杂度:O(n),只需要遍历一次字符串。
  • 空间复杂度:O(1),只使用了常数级的额外空间。

总结

两种解法都能有效解决回文串判断问题:

解法一逻辑清晰,易于理解,但需要额外的空间存储处理后的字符串;

解法二原地操作,空间复杂度更低,更适合处理大规模数据。在实际应用中,可以根据具体需求选择合适的解法。

相关推荐
MPCTHU3 分钟前
二叉树、排序算法与结构图
数据结构·算法·排序算法
亓才孓9 分钟前
[leetcode]树的操作
算法·leetcode·职场和发展
王禄DUT19 分钟前
化学方程式配平 第33次CCF-CSP计算机软件能力认证
开发语言·c++·算法
wuqingshun31415922 分钟前
蓝桥杯 XYZ
数据结构·c++·算法·职场和发展·蓝桥杯
DreamByte35 分钟前
C++菜鸟教程 - 从入门到精通 第五节
开发语言·c++·算法
南玖yy42 分钟前
数据结构C语言练习(两个队列实现栈)
c语言·数据结构·算法
明朝百晓生1 小时前
【强化学习】【1】【PyTorch】【强化学习简介优化框架】
算法
loser~曹1 小时前
基于快速排序解决 leetcode hot215 查找数组中第k大的数字
数据结构·算法·leetcode
Dream it possible!1 小时前
LeetCode 热题 100_打家劫舍(83_198_中等_C++)(动态规划)
c++·算法·leetcode·动态规划
zhouziyi07011 小时前
【蓝桥杯14天冲刺课题单】Day 8
c++·算法·蓝桥杯