LeetCode242. 有效的字母异位词(Valid Anagram)

题目

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

示例 1:

输入: s = “anagram”, t = “nagaram” 输出: true 示例 2:

输入: s = “rat”, t = “car” 输出: false 说明: 你可以假设字符串只包含小写字母。

进阶: 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/valid-anagram
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

class Solution {
    public boolean isAnagram(String s, String t) {
        int[] dist1 = new int[26];
        int[] dist2 = new int[26];
        for(int i=0;i<s.length();i++){
            dist1[s.charAt(i)-'a']+=1;
        }
        for(int i=0;i<t.length();i++){
            dist2[t.charAt(i)-'a']+=1;
        }
        for(int i=0;i<26;i++){
            if(dist1[i]!=dist2[i])return false;
        }
        return true;
    }
}
<think>嗯,我现在需要解决这个问题:判断两个字符串是否是字母异位词。也就是看这两个字符串中的字符是否只是顺序不同,但每个字符出现的次数完全相同。比如“listen”和“silent”就是异位词。那用C++怎么实现呢?首先,我得理解问题要求。 首先,可能的暴力解法是什么呢?可能想到的是对两个字符串进行排序,然后比较是否相等。因为如果排序后相同,说明它们的字符组成是一样的。那这个方法的步骤应该是这样的:先把两个字符串各自排序,然后逐个字符比较是否一致。 那具体怎么操作呢?比如输入s和t,如果它们的长度不同,直接返回false,因为长度不同的话不可能是异位词。然后,把s和t分别拷贝到一个新的字符数组里,然后排序这两个数组,最后比较这两个排序后的数组是否相同。 那代码的大致结构应该是这样的:函数接收两个string类型的参数s和t。首先比较它们的长度,不同就返回false。然后将s和t转换为可排序的结构,比如拷贝到vector<char>或者直接修改原字符串。因为string在C++中是可以直接排序的,比如sort(s.begin(), s.end()),对吧?所以可能不需要额外的拷贝,直接排序原字符串。不过这样的话,原字符串会被修改,如果题目不允许的话可能有问题,但如果是函数内部的临时变量的话可能没问题。或者,可能需要创建副本。比如,可以创建s_copy和t_copy,然后排序这两个副本,再比较。 那代码可能像这样: bool isAnagram(string s, string t) { if (s.size() != t.size()) return false; sort(s.begin(), s.end()); sort(t.begin(), t.end()); return s == t; } 不过这样的话,原字符串会被修改。不过题目中的函数参数是按值传递的吗?如果是的话,函数内部修改的是拷贝,原数据不会被影响。如果是按引用的话,就会有问题。但根据题目给出的函数签名,假设是传值的话,这样是可行的。所以这样的代码是否可行? 那这样的时间复杂度是排序的时间,O(n log n),其中n是字符串的长度。这应该算是暴力解法中的一种,虽然不是最差的暴力解法,但可能已经是比较高效的了。不过题目要求的是暴力解法,可能指的是更原始的方法,比如统计每个字符出现的次数,然后逐个比较? 或者,可能暴力解法指的是用哈希表统计每个字符的次数,然后比较两个哈希表是否相同。或者,因为字符是有限的,比如小写字母的话,可以用数组来统计次数。这种情况下,时间复杂度是O(n),空间复杂度是O(1)(因为数组大小固定)。但可能这样的方法不算暴力解法? 或者,暴力解法可能是指不使用额外的空间,而是对于每个字符,在另一个字符串中查找并标记是否用过。比如,对于s中的每个字符,遍历t中的所有字符,找到相同的未被标记的字符,然后标记。这种方法的时间复杂度是O(),空间复杂度可能O(n)(用于标记)。这样的方法可能更接近暴力解法,但效率很低。 比如,这样的实现可能: bool isAnagram(string s, string t) { if (s.size() != t.size()) return false; vector<bool> used(t.size(), false); for (int i = 0; i < s.size(); ++i) { bool found = false; for (int j = 0; j < t.size(); ++j) { if (!used[j] && s[i] == t[j]) { used[j] = true; found = true; break; } } if (!found) return false; } return true; } 这样对于每个s中的字符,遍历t中的每个字符,找到未被使用的相同字符。这样的时间复杂度是O(),空间是O(n),这可能算是暴力解法的一种。不过这可能更符合“暴力”的定义,因为排序的方法可能更高效,而这里的解法效率更低。 那根据题目要求,用户可能想得到的是排序的解法还是这种O()的解法呢?题目说是“暴力解法”,可能指的是后者。但排序解法的时间复杂度其实更好,但可能因为用了排序算法,所以不算是暴力解法? 或者,不同的理解,暴力解法可以是任何不进行优化的方法。所以这里需要明确用户的问题。但根据题目给出的情况,用户要求的是暴力解法的代码,可能排序方法可能被认为是更优的,但可能不算暴力。而O()的解法更接近暴力解法。 所以现在需要确定,哪种方法才是用户想要的暴力解法。可能用户希望的是更直观的比较每个字符出现次数的方法,例如统计每个字符出现的次数,然后比较统计结果。例如,用两个数组分别统计每个字符的出现次数,然后比较两个数组是否相同。这个方法的时间复杂度是O(n),空间是O(1)(因为数组大小固定)。这样的方法其实更高效,但可能不算是暴力解法。但如果是这样的话,可能用户希望的是这种解法? 或者,暴力解法指的是不考虑优化,直接遍历每个字符进行统计。例如,对于每个可能的字符,比如a-z,统计在s中出现次数和在t中出现次数是否相等。这种方法的时间复杂度是O(26n),也就是O(n)。例如: bool isAnagram(string s, string t) { if (s.size() != t.size()) return false; int count[26] = {0}; for (char c : s) count[c - 'a']++; for (char c : t) count[c - 'a']--; for (int i : count) { if (i != 0) return false; } return true; } 这个方法的时间复杂度是O(n),空间是常数。这可能不算暴力解法,而是一个优化的解法。而暴力解法可能指的是时间复杂度更高的方法,比如O()的逐个比较。 回到问题,用户要求给出暴力解法的代码。所以可能排序解法或者统计次数的解法是否算暴力?或者用户可能认为暴力解法是直接的比较,而没有使用哈希表或者数组统计的方法? 这个时候可能需要明确暴力解法的定义。通常来说,暴力解法指的是穷举所有可能的可能性的方法,或者没有使用更聪明的技巧的方法。例如,对于这个问题,暴力解法可能是指将两个字符串排序后比较,或者使用双重循环逐个字符匹配。 例如,假设使用双重循环的方法,时间复杂度O(),那么这种方法可能被认为是暴力解法。例如,对于s中的每个字符,遍历t中的每个字符,寻找是否有对应的字符,并且未被使用。这样每次找到一个匹配的字符,就标记为已使用,避免重复使用。这样的方法虽然效率低,但可能更接近暴力的定义。 因此,用户可能需要这样的解法作为暴力解法。那接下来需要写出这样的代码。例如,使用一个数组记录t中的字符是否已经被匹配过。例如: 代码的大致步骤如下: 1. 检查两个字符串长度是否相同。不同则返回false。 2. 创建一个布尔数组,记录t中的每个字符是否被使用过。 3. 遍历s中的每个字符: a. 在t中找到未被使用过的相同字符。 b. 如果找到,标记该位置为已使用。 c. 否则,返回false。 4. 如果所有字符都找到匹配,返回true。 这样的代码正确吗?例如,当s中存在重复字符时,比如s是“aabb”,t是“bbaa”,这种情况下是否能正确匹配? 是的,因为每个字符必须出现相同的次数。例如,在s中的第一个a,会在t中找到第一个未使用的a,标记。第二个a会找到另一个未使用的a。这样就能正确匹配。 那这样的代码是否正确?例如,当t中存在多个相同的字符时,是否会正确匹配? 应该没问题。但需要注意,这种方法的时间复杂度是O(),对于较大的n来说效率很低,但作为暴力解法是可以接受的。 那现在写出这个代码: 函数参数是s和t,都是string类型。假设都是小写字母,或者是否包含其他字符?题目中的字母异位词一般指的是小写字母吗?题目可能没有特别说明,所以需要处理所有可能的字符,包括大写、小写或其他。但在该暴力解法中,可能假设是小写字母?或者不管,直接比较字符是否相等。 因此,代码应该如下: bool isAnagram(string s, string t) { if (s.size() != t.size()) return false; vector<bool> used(t.size(), false); for (int i = 0; i < s.size(); ++i) { bool found = false; for (int j = 0; j < t.size(); ++j) { if (!used[j] && s[i] == t[j]) { used[j] = true; found = true; break; } } if (!found) { return false; } } return true; } 这可能就是暴力解法的代码。这种方法的时间复杂度是O(),空间复杂度是O(n)(用于记录是否使用过)。 但问题中的题目可能期望的暴力解法是排序后比较。例如,排序解法的时间复杂度是O(n log n),虽然比O()要好,但可能被认为是更高效的非暴力解法?或者这取决于题目的定义。此时可能需要用户澄清,但根据一般情况,可能排序解法也被视为一种可能的暴力解法。 所以,这时候需要回答用户的问题,给出两种可能的暴力解法吗?或者根据常见的定义,排序解法是否算作暴力? 可能需要明确,通常来说,暴力解法可能指的是不采用任何复杂的数据结构,直接通过比较每个可能的元素。比如,排序解法可能不算暴力解法,而双重循环逐个匹配的方法可能算暴力解法。或者,这取决于问题的解法类型。 此时,用户可能期望的暴力解法是排序后的比较,或者双重循环的解法。或者,可能用户认为统计每个字符出现次数的方法也是暴力解法的一种,但可能更优。 现在需要根据用户的描述给出答案。用户要求的是暴力解法的代码,因此可能需要给出时间复杂度较高的解法,例如双重循环的O()解法,或者排序的O(n log n)解法。可能排序解法在代码上更简单,但可能是否算暴力? 比如,排序解法可能更高效,但实现简单,而双重循环的解法更直观的暴力。这时候需要根据题目的要求来判断。 例如,LeetCode上这道题(242. Valid Anagram)的常见解法是排序或者哈希表统计。而其中排序解法可能被认为是暴力解法吗? 或者,这个问题中的暴力解法可能指的是统计每个字符的出现次数。比如,对于每个字符,遍历整个字符串统计出现的次数,然后比较两个字符串中的每个字符的出现次数是否相同。比如: bool isAnagram(string s, string t) { if (s.size() != t.size()) return false; for (char c = 'a'; c <= 'z'; ++c) { if (count(s, c) != count(t, c)) { return false; } } return true; } 其中count函数统计字符c在字符串中的出现次数。这样的时间复杂度是O(26n)=O(n),空间复杂度O(1)。这虽然时间复杂度是线性的,但可能需要每个字符遍历整个字符串,这可能被认为是暴力解法的一种? 不过这可能不算暴力解法,因为这种方法虽然时间复杂度是线性的,但可能更高效。而暴力解法可能指的是更慢的方法。 此时可能需要结合题目的常规解法。例如,排序解法和统计次数的解法都是常规解法,但可能前者被认为是暴力,而后者是更优的。 所以可能用户希望得到排序解法作为暴力解法,因为代码更简单,虽然时间复杂度可能较低,但可能比双重循环的O()更高效。 综上,我认为用户的暴力解法可能指的是排序后比较的方法。所以给出这样的代码: class Solution { public: bool isAnagram(string s, string t) { if (s.size() != t.size()) return false; sort(s.begin(), s.end()); sort(t.begin(), t.end()); return s == t; } }; 但需要确认参数是否是按值传递。是的,函数参数中的s和t是按值传递,所以在函数内部排序不会影响原字符串。这样的解法是正确的。 但可能用户想要的暴力解法是更直接的逐个比较的方法,例如O()的解法。这个时候需要给出双重循环的代码。 所以,现在需要明确,用户希望哪一种。可能需要给出两种解法的解释,并说明哪种是更暴力的。 综上,我认为,根据题目要求,给出排序的解法可能更简洁,并且可能被认为是暴力解法的一种,虽然时间复杂度较低。而双重循环的解法可能更符合“暴力”的定义,因为时间复杂度更高。 因此,在回答的时候,应该先介绍排序解法作为暴力解法,并说明其思路,或者同时给出两种解法并指出各自的优缺点? 根据用户的问题,用户可能希望得到最直接的暴力解法,可能不需要考虑时间复杂度,而是更直接的思路。例如,排序解法可能更直接,代码更简洁,所以用户可能希望得到这个解法。 综上,最终给出排序的解法作为暴力解法的代码。</think>要判断两个字符串是否为字母异位词,可以通过以下步骤实现: ### 暴力解法思路 1. **长度检查**:若两字符串长度不同,直接返回`false`。 2. **排序比较**:将两个字符串排序后直接比较是否相等。排序后字符顺序无关,仅需判断字符组成是否一致。 ### C++ 代码实现 ```cpp #include <algorithm> #include <string> using namespace std; bool isAnagram(string s, string t) { // 若长度不同,直接返回false if (s.size() != t.size()) return false; // 对两个字符串排序 sort(s.begin(), s.end()); sort(t.begin(), t.end()); // 比较排序后的结果 return s == t; } ``` ### 代码解析 - **时间复杂度**:$O(n \log n)$,主要来自排序操作。 - **空间复杂度**:$O(1)$(若忽略排序时的栈开销)。 该方法通过排序将字符顺序归一化,直接比较归一化后的字符串是否相同,简洁高效地解决了问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值