Leetcode 141 最长公共前缀 | 罗马数字转整数

1 题目

14. 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 ""

示例 1:

复制代码
输入:strs = ["flower","flow","flight"]
输出:"fl"

示例 2:

复制代码
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。

提示:

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] 如果非空,则仅由小写英文字母组成

2 代码实现

c++

cpp 复制代码
class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        if (strs.empty()){
            return "" ;
        } 

        string prefix = strs[0];

        for (int i = 1 ; i < strs.size() ; ++ i){
            int j = 0 ; 
            while (j < prefix.size() && j < strs[i].size() && prefix[j] == strs[i][j]){
                j++;
            }

            prefix = prefix.substr(0,j);
            if (prefix.empty()){
                break;
            }
        }
        return prefix ;
    }
};

js

javascript 复制代码
/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function(strs) {
    if (strs.length === 0 ){
        return "";
    }

    let prefix = strs[0];
    for (let i  = 1 ; i < strs.length ; ++i){
        let j = 0 ;
        while (i < prefix.length && j < strs[i].length && prefix[j] === strs[i][j]){
            j++;
        }
        prefix = prefix.substring(0,j);
        if (prefix.length === 0 ){
            break ;
        }
    }
    return prefix ;
};

思考

这个应该怎么做?暴力两两比较遍历?怎么存到前缀的,维护一个什么,hash表吗还是什么。

应该不是hash表,hash表是一一映射查询的。

题解

详细解题思路

要解决最长公共前缀问题,核心思路是以第一个字符串为基准,逐字符与其他字符串对比,逐步缩短公共前缀的长度,直到找到所有字符串共有的最长前缀。具体步骤:

  1. 处理边界情况:如果字符串数组为空,直接返回空字符串;如果只有一个字符串,直接返回该字符串。
  2. 以第一个字符串作为初始公共前缀。
  3. 遍历数组中剩余的每个字符串,逐个字符对比当前公共前缀和该字符串:
    • 找到两者的公共前缀长度(对比到第一个不同字符为止)。
    • 更新公共前缀为该长度的子串。
    • 如果公共前缀缩短为空,直接返回空(提前终止,优化效率)。
  4. 遍历结束后,剩余的公共前缀即为答案。

C++ 代码实现(LeetCode 可直接提交)

cpp 复制代码
#include <vector>
#include <string>
using namespace std;

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        // 边界情况1:数组为空,返回空字符串
        if (strs.empty()) {
            return "";
        }
        // 初始公共前缀为第一个字符串
        string prefix = strs[0];
        
        // 遍历数组中剩余的每个字符串
        for (int i = 1; i < strs.size(); ++i) {
            // 找到当前prefix和strs[i]的公共前缀长度
            int j = 0;
            // 条件:j不超过prefix长度、j不超过当前字符串长度、字符相等
            while (j < prefix.size() && j < strs[i].size() && prefix[j] == strs[i][j]) {
                j++;
            }
            // 截断prefix为公共部分
            prefix = prefix.substr(0, j);
            // 提前终止:如果prefix为空,无需继续遍历
            if (prefix.empty()) {
                break;
            }
        }
        return prefix;
    }
};

代码关键部分解释

  1. 边界处理if (strs.empty()) return ""; 处理数组为空的情况,符合题目中 "无公共前缀返回空" 的要求。
  2. 初始前缀string prefix = strs[0]; 把第一个字符串作为初始公共前缀,后续逐步缩短。
  3. 逐字符对比
    • while (j < prefix.size() && j < strs[i].size() && prefix[j] == strs[i][j]):循环条件保证不越界,且字符相等时继续。
    • prefix = prefix.substr(0, j);:截断前缀到公共部分(j 是第一个不同字符的位置)。
  4. 提前终止if (prefix.empty()) break; 一旦前缀为空,直接退出循环,避免无效遍历。

测试用例验证

示例 1

输入:strs = ["flower","flow","flight"]

  • 初始 prefix = "flower"
  • 对比 "flow":j 走到 4("flow" 长度),prefix 变为 "flow"
  • 对比 "flight":j 走到 2(prefix [2] = 'o',flight [2] = 'i',不相等),prefix 变为 "fl"
  • 输出:"fl"
示例 2

输入:strs = ["dog","racecar","car"]

  • 初始 prefix = "dog"
  • 对比 "racecar":j 走到 0(第一个字符 'd' != 'r'),prefix 变为 ""
  • 提前终止循环,输出:""

复杂度分析

  • 时间复杂度:O (m*n),m 是字符串的平均长度,n 是字符串数组的长度。每个字符最多对比一次,无冗余操作。
  • 空间复杂度:O (1),仅使用了常数级别的额外空间(除了存储结果的 prefix)。

总结

  1. 核心逻辑:以第一个字符串为基准,逐字符串对比、逐步缩短公共前缀,是解决该问题最直观且高效的方法。
  2. 优化点:提前终止循环(前缀为空时直接退出),避免无效遍历,提升实际运行效率。
  3. 边界处理:必须考虑数组为空、字符串长度为 0 的情况,确保代码鲁棒性。

注意substr substring 方法截取公共长度。

3 题目

13. 罗马数字转整数

罗马数字包含以下七种字符: IVXLCDM

复制代码
字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

示例 1:

复制代码
输入: s = "III"
输出: 3

示例 2:

复制代码
输入: s = "IV"
输出: 4

示例 3:

复制代码
输入: s = "IX"
输出: 9

示例 4:

复制代码
输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

复制代码
输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= s.length <= 15
  • s 仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
  • 题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999]
  • 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
  • IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
  • 关于罗马数字的详尽书写规则,可以参考 罗马数字 - 百度百科

4 代码实现

c++

cpp 复制代码
class Solution {
public:
    int romanToInt(string s) {
        unordered_map <char,int > romanMap = {
            {'I', 1},
            {'V', 5},
            {'X', 10},
            {'L', 50},
            {'C', 100},
            {'D', 500},
            {'M', 1000}
        };

        int result = 0 ;
        int n = s.size();

        for (int i = 0 ; i < n ; ++i){
            if (i < n - 1 && romanMap[s[i]] < romanMap[s[i+1]]){
                result -= romanMap[s[i]];
            } else {
                result += romanMap[s[i]];
            }
        }
        return result ;
    }
};

js

javascript 复制代码
/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    const romanMap = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000
    };   
    let result = 0 ;
    const n = s.length ;

    for (let i = 0 ; i < n ; i ++ ){
    if (i < n && romanMap[s[i]] < romanMap[s[i + 1 ]]){
    result -= romanMap[s[i]];
        }else{
            result += romanMap[s[i]];
        }
    }
    return result ;
};

思考

???其实我苯人都不会写罗马数字的说。。。。

不行啊我没理解这些东西啊。。。。。。。。。。。。好像是两两一组,左边大于右边就相加,否则就相减,奇数个的时候高位单出。

题解

别着急!罗马数字转整数的核心逻辑其实很简单,我会用最易懂的方式给你讲解,再配上完整的 C++ 代码和详细注释,保证你能看懂~

核心思路(先帮你理清逻辑)

你理解的 "两两一组比较" 方向是对的,但更准确的规则是:

  1. 基本规则 :罗马数字的字符对应固定数值,正常情况下(小数字在大数字右边)直接累加即可(比如 VI = 5+1=6)。
  2. 特殊规则 :如果当前字符的数值 < 下一个字符的数值 ,说明是特殊组合(比如 IVIX),需要用「下一个数 - 当前数」,且当前数要 (比如 IV = 5-1=4,等价于 累加时先减 1,再加 5)。
  3. 简化实现 :遍历每个字符时,先判断当前字符和下一个字符的大小:
    • 如果 当前值 < 下一个值 → 结果 当前值
    • 否则 → 结果 当前值
    • 最后一个字符直接加(因为没有下一个字符)。

C++ 完整实现(带详细注释)

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;

class Solution {
public:
    int romanToInt(string s) {
        // 第一步:建立罗马字符到数值的映射表(哈希表),方便快速查找
        unordered_map<char, int> romanMap = {
            {'I', 1},
            {'V', 5},
            {'X', 10},
            {'L', 50},
            {'C', 100},
            {'D', 500},
            {'M', 1000}
        };

        int result = 0; // 存储最终结果
        int n = s.size(); // 字符串长度

        // 第二步:遍历每个字符
        for (int i = 0; i < n; ++i) {
            // 关键判断:如果当前字符不是最后一个,且当前值 < 下一个值 → 减当前值
            if (i < n - 1 && romanMap[s[i]] < romanMap[s[i+1]]) {
                result -= romanMap[s[i]];
            } else {
                // 否则(最后一个字符 / 当前值 ≥ 下一个值)→ 加当前值
                result += romanMap[s[i]];
            }
        }

        return result;
    }
};

// 测试用例(可以直接运行)
int main() {
    Solution sol;
    // 示例1:III → 3
    cout << sol.romanToInt("III") << endl;
    // 示例2:IV → 4
    cout << sol.romanToInt("IV") << endl;
    // 示例3:IX → 9
    cout << sol.romanToInt("IX") << endl;
    // 示例4:LVIII → 58(50+5+3)
    cout << sol.romanToInt("LVIII") << endl;
    // 示例5:MCMXCIV → 1994(1000 + (1000-100) + (100-10) + (5-1))
    cout << sol.romanToInt("MCMXCIV") << endl;
    return 0;
}

关键逻辑拆解(以 MCMXCIV 为例)

我们一步步算 MCMXCIV(1994),帮你理解:

字符 索引 当前值 下一个值 判断(当前 < 下一个?) 操作 结果累计
M 0 1000 100 +1000 1000
C 1 100 1000 -100 900
M 2 1000 10 +1000 1900
X 3 10 100 -10 1890
C 4 100 1 +100 1990
I 5 1 5 -1 1989
V 6 5 +5 1994

总结

  1. 核心规则:遍历每个字符,若当前值 < 下一个值则减,否则加,最后一个字符直接加。
  2. 关键工具 :用哈希表(unordered_map)存储字符 - 数值映射,实现 O (1) 时间查找。
  3. 复杂度:时间复杂度 O (n)(n 是字符串长度),空间复杂度 O (1)(映射表大小固定)。

5 小结

马后炮之总是觉得写了就不难,还是多多反思,多多理解!!!加油,坚持!

相关推荐
InfiniSynapse2 小时前
连上Snowflake就能取数:InfiniSynapse + Spider2-Snow实战企业数据分析
数据结构·图像处理·人工智能·算法·语言模型·数据挖掘·数据分析
少许极端2 小时前
算法奇妙屋(三十三)-DFS的记忆化搜索
算法·深度优先·记忆化搜索
WolfGang0073213 小时前
代码随想录算法训练营 Day13 | 二叉树 part03
数据结构·算法·leetcode
进击的小头3 小时前
第11篇:频率响应绘制方法——伯德图(Bode Plot)
python·算法
2401_883035463 小时前
C++20概念(Concepts)入门指南
开发语言·c++·算法
fengci.3 小时前
PolarD&N困难补充
算法
91刘仁德3 小时前
C++ 内存管理
android·c语言·数据结构·c++·经验分享·笔记·算法
sali-tec3 小时前
C# 基于OpenCv的视觉工作流-章38-BF特征匹配
图像处理·人工智能·opencv·算法·计算机视觉
历程里程碑3 小时前
链表-----
数据结构·线性代数·算法·链表·矩阵·lua·perl