Day 14。
今天两道题分别是 Find the Index of the First Occurrence in a StringDivide Two Integers
一道是 字符串匹配入门题,一道是 位运算 + 倍增思想

今天的关键词:匹配、暴力搜索、倍增、位移、溢出处理


🧠 LeetCode 28 – Find the Index of the First Occurrence in a String

💡 思路总结

这题要求在字符串 haystack 中找到 needle 第一次出现的位置。
如果不存在,就返回 -1

你的做法很直接:

  • 枚举 haystack 的每一个位置 i
  • 如果 haystack[i] == needle[0],就从这里开始尝试匹配
  • 如果整段 needle 都匹配成功,就返回当前位置 i

这其实就是最经典的:

暴力字符串匹配


💻 代码实现

class Solution {
public:
    int strStr(string haystack, string needle) {
        //同样是快慢指针;
        for(int i = 0;i < haystack.size();i++){
            if(haystack[i] == needle[0]){
                //开始配对
                int check = i;
                int check_right = 0;
                for(char c : needle){
                    if(haystack[check] != c)break;
                    else{
                        check++;
                        check_right++;
                    }
                }
                if(check_right == needle.size())return i;

            }
        }
        return -1;
    }
};

📌 知识点总结

1️⃣ 这题本质就是暴力匹配

思路可以概括成一句话:

从主串的每个位置出发,试试看能不能完整匹配模式串

这是一种很基础但非常重要的字符串题思路。


2️⃣ 两层逻辑很清楚

你的代码可以拆成两层:

  • 外层:枚举起点
  • 内层:检查从这个起点开始能否完整匹配

这种写法逻辑非常自然,也很适合刚接触字符串匹配时理解。


3️⃣ 这里有一个边界问题要注意

你这份代码整体思路是对的,但有一个隐藏风险:

if(haystack[check] != c) break;

这里默认 check 一定没有越界。
但如果 needle 比剩余的 haystack 更长,就可能访问到越界位置。

比如:

haystack = "abc"
needle = "cd"

i = 2 时,haystack[2] == 'c',会进入内层,接着 check++ 后就可能访问越界。


✅ 更稳一点的版本

更规范的写法一般会把起点限制在:

i <= haystack.size() - needle.size()

这样后面就不会越界。

class Solution {
public:
    int strStr(string haystack, string needle) {
        if (needle.empty()) return 0;

        for (int i = 0; i <= (int)haystack.size() - (int)needle.size(); i++) {
            int j = 0;
            while (j < needle.size() && haystack[i + j] == needle[j]) {
                j++;
            }
            if (j == needle.size()) return i;
        }

        return -1;
    }
};

4️⃣ 这题是 KMP 的前置题

这题用暴力法完全可以做出来。
不过它还有更高级的做法:

KMP 字符串匹配

但现阶段先把暴力匹配理解透就很不错,因为很多复杂算法都是从基础方法优化出来的。


⚙️ LeetCode 29 – Divide Two Integers

💡 思路总结

这题要求在不能使用乘法、除法和取模的情况下,实现整数相除。

这题看起来有点吓人,但核心思路其实很巧:

用减法模拟除法,但不能一下一下减,要用“倍增”优化

比如想算:

43 / 5

朴素想法是:

  • 43 – 5 – 5 – 5 …
  • 看能减多少次

但这样太慢。
所以更聪明的方法是:

  • 先减 5
  • 再减 10
  • 再减 20
  • 也就是每次尽量减去当前能减的最大 2^k * divisor

这就是你代码里的核心思想。


💻 代码实现

class Solution {
public:
    int divide(int dividend, int divisor) {
        if (dividend == INT_MIN && divisor == -1) {
            return INT_MAX;
        }

        long long a = dividend;
        long long b = divisor;

        bool negative = (a < 0) ^ (b < 0);

        a = llabs(a);
        b = llabs(b);

        long long result = 0;

        while (a >= b) {
            long long temp = b;
            long long count = 1;

            while ((temp << 1) <= a) {
                temp <<= 1;
                count <<= 1;
            }

            a -= temp;
            result += count;
        }

        return negative ? -result : result;
    }
};

📌 知识点总结

1️⃣ 为什么不能直接一直减

如果每次只减一个 divisor,比如:

1000000000 / 1

那要减十亿次,显然会超时。

所以必须优化。


2️⃣ 倍增思想是这题灵魂

你这里用了:

while ((temp << 1) <= a) {
    temp <<= 1;
    count <<= 1;
}

意思是:

  • 当前先从 divisor 开始
  • 能翻倍就一直翻倍
  • 直到不能再翻倍为止

这样每次都减去“尽可能大的一块”,效率就高很多。

这和二进制拆分的思想很像。


3️⃣ count 的作用

temp 翻倍时:

  • temp = divisor * 2^k

那么对应商也要加上:

  • count = 2^k

所以最后:

result += count;

这一句其实就是在累计商。


4️⃣ 先统一转正数很聪明

你这里先用:

bool negative = (a < 0) ^ (b < 0);

判断最后结果是否为负数。

然后:

a = llabs(a);
b = llabs(b);

把两边都转成正数处理。

这样好处很大:

  • 主逻辑更统一
  • 不需要在循环里反复考虑正负号
  • 最后再统一加上符号即可

5️⃣ 溢出特判不能少

这题最经典的坑点就是:

INT_MIN / -1

理论结果是:

2147483648

已经超过 int 范围了,所以必须特判:

if (dividend == INT_MIN && divisor == -1) {
    return INT_MAX;
}

这一步非常重要,也几乎是这题的必考点。


🧩 今日总结

题目核心思想
LeetCode 28暴力字符串匹配 + 边界控制
LeetCode 29倍增 + 位运算 + 减法模拟除法

今天两题风格差别挺大:

  • 28 比较偏基础字符串处理
  • 29 更偏思维转换和数学优化

但它们都在提醒一件事:

题目表面要求是一回事,真正关键是把问题转化成你熟悉的形式。


🧠 今日收获

今天最值得记住的两个模型:

1. 字符串匹配基础模型

看到这种“找子串”的题,先想到:

  • 枚举起点
  • 逐字符匹配
  • 注意边界别越界

这是最基本的字符串匹配框架。


2. 倍增优化模型

如果一个问题本来可以“重复减、重复加、重复操作”来做,
但朴素方法太慢,就要想到:

  • 能不能每次翻倍
  • 能不能按二进制拆分
  • 能不能一大块一大块处理

这类思想在很多题里都特别有用。


💬 写给自己的话

Day 14 了。

两周了。

你现在已经不是刚开始那个“刷题试试看”的状态了,
而是真的在一点点建立自己的算法直觉。

今天这种题很有价值:

  • 一题帮你继续巩固基础匹配思维
  • 一题逼你做思维转化和优化

这两种能力,后面都会反复用到。

别急着追求全都会。
你现在最重要的,是保持这种稳定的节奏:

  • 做题
  • 总结
  • 发现问题
  • 修正思路

时间久了,很多东西真的会内化成直觉。

阿玉,Day 14,已经坚持两周了。继续冲。

—— 刷题第 14 天