Day 6。
今天这两题是一组非常经典的“配套题”:

  • 12. Integer to Roman
  • 13. Roman to Integer

一道是 整数转罗马数字,一道是 罗马数字转整数
这种前后对应的题特别适合加深理解,因为你不仅要会“写出去”,还要会“读回来”。

今天的关键词:贪心、模拟、映射、特殊规则处理


🧠 LeetCode 12 – Integer to Roman

💡 思路总结

这题的核心其实很直接:

每次优先减去当前能匹配的最大罗马数字值

比如 1994

  • 先减 1000,得到 M
  • 再减 900,得到 CM
  • 再减 90,得到 XC
  • 再减 4,得到 IV

最后拼起来就是:

MCMXCIV

这就是一个很典型的 贪心思想


💻 代码实现

class Solution {
public:
    string intToRoman(int num) {
        vector<int> values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        vector<string> symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        string result;
        int num_ = num;
        while(num_ != 0){
            for(int i = 0 ; i<values.size();i++){
                
                if (num_ >= values[i]){
                    num_ = num_-values[i];
                    result += symbols[i];
                    break;
                }
            }
        }
        return result;
      
    }
};

📌 知识点总结

1️⃣ 贪心思想

这题很适合理解贪心:

  • 当前能选最大的,就先选最大的
  • 每次都尽快让剩余值减少

因为罗马数字的规则本身就是固定组合,所以这种贪心是成立的。


2️⃣ 特殊值要提前放进去

你这里把这些值都提前放进数组里了:

900 -> CM
400 -> CD
90  -> XC
40  -> XL
9   -> IX
4   -> IV

这一步非常关键。
因为罗马数字不是单纯地一直拼 IXC,它有减法表示法。

比如:

  • 4 不是 IIII
  • 9 不是 VIIII

而是:

  • IV
  • IX

所以这些特殊组合必须直接参与匹配。


3️⃣ 数据与规则绑定

这里用两个数组:

  • values
  • symbols

本质是在做“值和表示法的映射”。

这种写法很常见,尤其适合规则固定、顺序明确的模拟题。


🏛️ LeetCode 13 – Roman to Integer

💡 思路总结

这一题反过来,要把罗马数字转成整数。

你的思路是:

先判断两个字符能不能匹配特殊组合,再判断单个字符

这个想法是对的,而且非常关键。

因为像:

  • IV
  • IX
  • XL
  • XC
  • CD
  • CM

这些必须优先作为整体识别。

不然你如果先按单字符处理,IV 就会被拆成:

  • I = 1
  • V = 5

结果变成 6,显然错了。


💻 代码实现

class Solution {
public:
    int romanToInt(string s) {
        vector<int> values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
        vector<string> symbols = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
        int result = 0;
        //应该先判断两个,在判断一个
        for(int i = 0 ; i < s.size(); i ++){
            bool match = false;
            string new_ = s.substr(i,2);
            for(int j = 0 ;j < symbols.size(); j ++){
                if(new_ ==symbols[j]){
                    result += values[j];
                    i++;
                    match = true;
                    break;
                }
            }
            if(!match){
                string new_ = s.substr(i,1);
                for (int k = 0;k<symbols.size();k++){
                    if(new_ ==symbols[k]){
                        result += values[k];
                        match = true;
                        break;
                    }


                }
            }



        }
        return result;

        
    }
};

📌 知识点总结

1️⃣ 优先匹配长的

这是这题的灵魂:

先看两个字符,再看一个字符

因为双字符组合优先级更高。

这个思路在字符串题里也很常见:
当存在“长匹配”和“短匹配”冲突时,往往要优先处理更长的那个。


2️⃣ substr 的使用

你这里用了:

s.substr(i, 2)
s.substr(i, 1)

这个写法很直观,逻辑也清晰。
对当前阶段来说,非常适合做题和理解。


3️⃣ 模拟题的本质

这题本质还是一题规则模拟:

  • 按顺序扫描字符串
  • 判断当前应该匹配哪种模式
  • 匹配成功就更新结果和下标

所以这类题最重要的不是“玄学技巧”,而是你能不能把规则拆清楚。


🧩 两题放在一起看

今天这两题特别适合放在一起理解:

题目核心思想
LeetCode 12贪心 + 枚举最大匹配值
LeetCode 13模拟 + 优先匹配双字符

它们本质上都在做同一件事:

用固定规则表,完成数值和字符串之间的转换


🧠 今日收获

今天可以重点记住这两个模型。

1. 固定规则转换题

如果题目规则是固定且有限的,可以考虑:

  • 建表
  • 按顺序匹配
  • 映射转换

这类写法非常稳。


2. 特殊情况前置

很多题的难点不是主体逻辑,而是特殊规则:

  • 罗马数字里的减法表示
  • 双字符优先匹配
  • 不能只按单个字符贪算

这提醒我们一件事:

写题时要先想“有没有特殊规则”,再写主逻辑。


✍️ 一个可以继续优化的小点

你这两题都已经能过,而且思路也很正。

不过博客里可以顺带记一下一个提升点:

LeetCode 12

你现在是每次都从头扫一遍 values,没问题,但写法可以再简洁一点,比如:

for (int i = 0; i < values.size(); i++) {
    while (num >= values[i]) {
        num -= values[i];
        result += symbols[i];
    }
}

这样结构会更紧凑。


LeetCode 13

你现在是用双层循环查找匹配,也能做。
后面如果你想优化,可以考虑哈希表或字符值映射,让查找更快、代码更短。

但在当前阶段,先把规则理解透,比盲目追求最短代码更重要。


💬 写给自己的话

Day 6 了。

已经不是“试着刷一刷”的阶段了,
你现在是真的在持续积累。

今天这种成对出现的题很有价值,
因为你不只是做了一题,而是在练:

  • 正向转换怎么想
  • 逆向转换怎么处理
  • 规则题怎么拆
  • 特殊情况怎么优先判断

这比单纯 AC 更重要。

继续保持这个节奏。
一天天看起来很普通,
但题感、代码能力、边界意识,都是这样一点点堆起来的。

别小看每天这两题。
你现在写下的每一篇总结,都会在后面某一天变成你的底气。

阿玉,Day 6,继续冲。

—— 刷题第 6 天