1 题目
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
输入:strs = ["flower","flow","flight"]
输出:"fl"
示例 2:
输入:strs = ["dog","racecar","car"]
输出:""
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 2000 <= strs[i].length <= 200strs[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表是一一映射查询的。
题解
详细解题思路
要解决最长公共前缀问题,核心思路是以第一个字符串为基准,逐字符与其他字符串对比,逐步缩短公共前缀的长度,直到找到所有字符串共有的最长前缀。具体步骤:
- 处理边界情况:如果字符串数组为空,直接返回空字符串;如果只有一个字符串,直接返回该字符串。
- 以第一个字符串作为初始公共前缀。
- 遍历数组中剩余的每个字符串,逐个字符对比当前公共前缀和该字符串:
- 找到两者的公共前缀长度(对比到第一个不同字符为止)。
- 更新公共前缀为该长度的子串。
- 如果公共前缀缩短为空,直接返回空(提前终止,优化效率)。
- 遍历结束后,剩余的公共前缀即为答案。
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;
}
};
代码关键部分解释
- 边界处理 :
if (strs.empty()) return "";处理数组为空的情况,符合题目中 "无公共前缀返回空" 的要求。 - 初始前缀 :
string prefix = strs[0];把第一个字符串作为初始公共前缀,后续逐步缩短。 - 逐字符对比 :
while (j < prefix.size() && j < strs[i].size() && prefix[j] == strs[i][j]):循环条件保证不越界,且字符相等时继续。prefix = prefix.substr(0, j);:截断前缀到公共部分(j 是第一个不同字符的位置)。
- 提前终止 :
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)。
总结
- 核心逻辑:以第一个字符串为基准,逐字符串对比、逐步缩短公共前缀,是解决该问题最直观且高效的方法。
- 优化点:提前终止循环(前缀为空时直接退出),避免无效遍历,提升实际运行效率。
- 边界处理:必须考虑数组为空、字符串长度为 0 的情况,确保代码鲁棒性。
注意substr substring 方法截取公共长度。
3 题目
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。
字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做 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 <= 15s仅含字符('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++ 代码和详细注释,保证你能看懂~
核心思路(先帮你理清逻辑)
你理解的 "两两一组比较" 方向是对的,但更准确的规则是:
- 基本规则 :罗马数字的字符对应固定数值,正常情况下(小数字在大数字右边)直接累加即可(比如
VI= 5+1=6)。 - 特殊规则 :如果当前字符的数值 < 下一个字符的数值 ,说明是特殊组合(比如
IV、IX),需要用「下一个数 - 当前数」,且当前数要减 (比如IV= 5-1=4,等价于 累加时先减 1,再加 5)。 - 简化实现 :遍历每个字符时,先判断当前字符和下一个字符的大小:
- 如果
当前值 < 下一个值→ 结果 减 当前值 - 否则 → 结果 加 当前值
- 最后一个字符直接加(因为没有下一个字符)。
- 如果
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 |
总结
- 核心规则:遍历每个字符,若当前值 < 下一个值则减,否则加,最后一个字符直接加。
- 关键工具 :用哈希表(
unordered_map)存储字符 - 数值映射,实现 O (1) 时间查找。 - 复杂度:时间复杂度 O (n)(n 是字符串长度),空间复杂度 O (1)(映射表大小固定)。
5 小结
马后炮之总是觉得写了就不难,还是多多反思,多多理解!!!加油,坚持!