leetcode --- 438. 找到字符串中所有字母异位词[C++/滑动窗口+哈希表]

本文介绍了解决LeetCode问题438,如何在一个字符串中找出所有与目标字符串p的字符组成相同但顺序不同的异位词。利用哈希表和滑动窗口优化算法,统计两个字符串的字符出现次数进行匹配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题:438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

题目解析:

本题要在一个字符串中找另一个字符串的异位词。

这个异位词其实有个特点就是组成的元素都是相同的,只是顺序不同(类似化学同素异形体的概念)

如果用暴力查找枚举每一种排序情况是很麻烦的,换一种思路。

我们其实只要知道在同样长度的字符串中出现的字符个数是否和目标字符串(p)一样即可。

因为只要他们的构成元素一致,那么其中一个字符串必然是另一个字符串的异位词。

而要统计对比两个字符串的字符出现个数,可以用两个哈希表实现。

算法原理:

先用一个哈希表 hash_p 将p字符串的字符信息都录入

然后在s字符串上遍历。当

使用双指针维护遍历的字符串。保持其长度和p字符串一致,那么就可以发现,双指针一直是往右移动的。

所以可以用滑动窗口优化。且这个窗口大小是固定的。

此时只要比较两个哈希表中的字符个数是否一致即可。

因为比较两个哈希表需要每一次都遍历哈希表,进一步优化,设置一个有效数count来记录哈希表hash_s中有效的字符个数,即hash_p(目标字符串的哈希表)中有的,hash_s也有时就为有效。

这样,只要比较count和p字符串的长度大小即可。省去了每一次要遍历哈希表的时间。

直接进入滑动窗口四步走

1.进窗口

先将右指针指向的字符信息取出(保存到一个临时变量 char in;)。

在 哈希表hash_s 录入right指针指向的字符

如果在hash_s中这个字符记录的个数小于或等于 hash_p中的个数

那么表示窗口中的有效个数增加。

2.判断

如果窗口长度超过目标字符串长度时,要进行出窗口操作

3.出窗口

将左指针指向字符信息先取出来(保存到一个临时变量 char out;)。

然后左指针向右移动一位。

判断 如果出窗口字符 out(最开始保存的)在hash_s中的个数要小于等于在hash_p中的个数,表示这个出窗口的字符是有效字符,有效个数count减小一个(判断条件没有大于,是因为如果是大于的话,那么这个字符本身就是多余的,在先前的入窗口中就不会增加有效个数,所以这里也就没必要减小有效个数)

4.更新状态

当窗口内的有效个数等于p字符串的长度,表示找到异位词,将左指针的下标给返回值数组

代码编写:

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        
        int hash_s[26] = {0},hash_p[26] = {0};
        vector<int>ret;

        //初始化哈希表
        for(auto ch:p)
        {
            hash_p[ch - 'a']++;
        }
        int len = p.size();
        int count = 0;//存放有效个数
        for(int left = 0,right = 0;right < s.size();right++)
        {
            //进窗口
            char in = s[right];//right的更新在循环结束后,所以这里不++,先不移动right
            if(++hash_s[in - 'a'] <= hash_p[in-'a'])//先更新哈希表再比较
            {
                count++;
            }
            //判断
            if(right-left+1 > len) //因为是窗口大小是固定的,所以用if语句即可
            {
                //出窗口
                char out = s[left++];
                if(hash_s[out - 'a']-- <= hash_p[out - 'a'])//先比较再更新哈希表
                {
                    count--;
                }

            }
            //更新
            if(count == len)//有效数一致   ----- 为字母异位词
            {
                ret.push_back(left);
            }

        }
        return ret;
    }
};

<think>好的,我现在需要解决这个问题:给定两个字符串s和t,编写一个函数来判断t是否是s的字母异位词,但用户提到要在C++中使用“最长子串”来解决。这里可能存在一些理解上的问题,需要先理清楚。 首先,我需要明确什么是字母异位词。根据常见的定义,字母异位词指的是两个字符串包含的字符种类和每个字符的数量完全相同,只是排列顺序不同。例如,“listen”和“silent”就是字母异位词。 通常判断异位词的方法有两种:一是对两个字符串排序后比较是否相同;二是使用哈希表统计每个字符出现的次数,然后比较两个哈希表是否相同。这两种方法的时间复杂度通常是O(n log n)和O(n)(假设使用数组统计的话)。 但用户提到要使用“最长子串”来解决这个问题。这里可能存在误解,因为最长子串通常用于解决如“最长无重复子串”这样的问题,而判断异位词通常不需要用到子串的概念。可能用户混淆了不同的问题,或者有特定的解决思路需要结合子串来判断。 我需要确认用户的需求是否正确。或者,用户是否希望找到s中的一个子串,使其恰好是t的异位词?例如,题目可能是类似于LeetCode 438题“找到字符串中所有字母异位词”,这种情况下需要寻找所有符合条件的子串。但原题只是判断t是否是s的异位词,而用户可能误将问题描述和解决方法混淆了。 假设用户确实是想判断t是否是s的异位词,但希望用类似滑动窗口的方法(可能涉及子串比较)来处理,比如在s中寻找一个长度与t相同的子串,其字符计数与t相同。不过对于单纯的异位词判断来说,这种方法可能效率较低,但需要进一步分析。 接下来,我需要考虑在C++中如何实现。常规方法是使用哈希表或数组统计字符频率。例如,可以创建一个大小为26的数组(假设只有小写字母),统计每个字符的出现次数。对于字符串s,每个字符出现时增加计数;对于字符串t,每个字符出现时减少计数。最后检查数组是否全为零,如果是,则是异位词。 但如果用户坚持要用最长子串的方法,可能需要另一种思路。例如,假设s和t的长度相同,那么整个s是否可以通过排列得到t。这种情况下,比较它们的字符频率即可,不需要找子串。但若用户提到的“最长子串”是指某种特定解法,可能需要重新理解问题。 可能的误解是用户将异位词问题和寻找异位词子串问题混淆了。比如,用户可能想解决的是在s中找到包含t所有字符的最短子串(如LeetCode 76题),或者找到所有异位词的起始位置(如LeetCode 438题),但问题描述却是判断是否为异位词。因此,需要明确用户的实际需求。 为了处理这个问题,我需要先按照常规方法给出答案,并指出用户可能的误解。或者,假设用户确实希望用滑动窗口或子串相关的技巧来解决异位词判断问题,可能需要调整思路。 假设用户确实要求用最长子串的方法,那么可能的思路是:如果s和t是异位词,那么它们的最长公共子串长度应该等于它们的长度,但这可能不正确,因为子串需要连续,而异位词不一定是连续的。所以这可能行不通。 另一种可能是,用户误将滑动窗口方法用于异位词判断。例如,滑动窗口通常用于在s中寻找包含t所有字符的子串,但这与判断两个字符串是否为异位词不同。如果s和t长度不等,可以直接返回false;如果相等,则比较字符频率即可,无需滑动窗口。 综上,用户可能存在对问题或方法名称的混淆。正确的做法是首先检查两个字符串长度是否相同,不同则直接返回false。然后统计字符频率是否一致。但用户提到使用最长子串,可能希望用滑动窗口或子串比较的方法。 假设用户的问题描述有误,实际上是想在s中找t的异位词子串,那么问题类似于LeetCode 438。但原问题只是判断是否为异位词,所以需要澄清。 现在,我需要回答用户的问题,但用户可能混淆了问题。因此,在回答时,我需要先给出常规的异位词判断方法,然后解释为什么使用最长子串的方法可能不适用,或者可能用户的问题存在误解,并给出可能的正确解法。或者,如果用户确实希望用某种子串相关的技巧,例如滑动窗口,则需要调整思路。 可能的步骤: 1. 判断s和t长度是否相等,否则直接返回false。 2. 使用数组统计字符频率。 3. 比较两个数组是否相同。 但用户要求用最长子串,这可能不适用。因此,可能需要指出用户可能的误解,并给出正确的解法,同时解释滑动窗口通常用于不同的问题。 最终,正确的C++代码应该是统计字符频率的方法,而不是使用子串。因此,我需要明确回答用户的问题,指出正确的方法,并解释为什么最长子串的方法不适用,或者可能用户的问题有误。</think>要判断字符串 `t` 是否是 `s` 的字母异位词(即字符种类和数量相同,但顺序不同),常规的高效方法是**统计字符频率**。不过您提到“最长子串”,这可能是对问题或方法的混淆(最长子串通常用于解决其他问题,如找字符串中的最长重复子串或滑动窗口问题)。以下分步骤说明正确解法及常见误解: --- ### **一、字母异位词的定义** 若两个字符串满足: 1. 长度相等; 2. 每个字符的出现次数完全相同。 则它们是字母异位词。例如:`s = "anagram"`,`t = "nagaram"` 是异位词--- ### **二、常规解法(统计字符频率)** #### 步骤说明(C++实现) 1. **长度检查**:若 `s` 和 `t` 长度不等,直接返回 `false`。 2. **统计频率**:使用数组记录每个字符的出现次数。 3. **比较频率**:若所有字符频率一致,则为异位词。 ```cpp #include <string> using namespace std; bool isAnagram(string s, string t) { if (s.size() != t.size()) return false; // 长度不同直接返回 int count[26] = {0}; // 假设仅包含小写字母 for (char c : s) count[c - 'a']++; // 统计s的字符频率 for (char c : t) { count[c - 'a']--; // 遍历t时递减计数 if (count[c - 'a'] < 0) return false; // 若计数为负,说明t中存在额外字符 } return true; // 所有字符频率匹配 } ``` #### 复杂度分析 - **时间复杂度**:$O(n)$,仅需两次遍历。 - **空间复杂度**:$O(1)$,固定大小的数组。 --- ### **三、关于“最长子串”的误解分析** 您提到的“最长子串”可能混淆了以下两种问题: 1. **字母异位词判断**:直接统计字符频率即可。 2. **寻找异位词子串**(如 LeetCode 438):在长字符串中寻找所有满足异位词条件的子串起始位置,需用滑动窗口+频率统计。 若需解决后者(例如在字符串 `s` 中找到所有 `t` 的异位词子串),则需滑动窗口优化。但原问题仅需判断整个字符串是否为异位词,无需子串操作。 --- ### **四、滑动窗口解法的适用场景** 若题目改为“在 `s` 中是否存在一个子串是 `t` 的异位词”,则需滑动窗口: 1. 固定窗口大小为 `t` 的长度。 2. 滑动窗口遍历 `s`,检查每个窗口的字符频率是否与 `t` 匹配。 但这与原问题的需求不同,**原问题只需整体判断**。 --- ### **五、总结** - **正确方法**:直接统计字符频率,时间复杂度 $O(n)$。 - **误解纠正**:“最长子串”方法适用于子串搜索问题,而非整体异位词判断。 - **扩展建议**:若需解决子串异位词搜索问题(如 LeetCode 438),可进一步了解滑动窗口+哈希表的组合优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值