同字母异序词 python_LeetCode 49: 字母异位词分组Group Anagrams

LeetCode 49: 字母异位词分组Group Anagrams

题目:

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

Given an array of strings, group anagrams together.

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"],

输出:

[

["ate","eat","tea"],

["nat","tan"],

["bat"]

]

说明:

所有输入均为小写字母。

不考虑答案输出的顺序。

Note:

All inputs will be in lowercase.

The order of your output does not matter.

解题思路:

​题目要求是 不管字母怎样排序只要字母相同都归为一类, 只要把所有单词的字母按一定规律排列好, 只要每个单词的字母按规律排好后组成的字符串相同, 则归为一类

排序字母解题:

​用哈希映射 {Key : Value} Key 为排好序的字符串, Value 为数组, 存储与 Key 字母相同的单词, 遍历每个单词并排序字母, 查找排序好的字符串是否存在于 Keys, 利用哈希映射可将查找操作时间复杂度降为 O(1)

其解题逻辑为(这里按字母升序排列):

输入: ["eat", "tea", "tan", "ate", "nat", "bat"]

建立哈希映射 map = {}

遍历该字符串数组:

第一个单词: "eat" --> "aet"

"aet" 不存在于 map, 建立映射 {"aet" : [ "eat" ] }

第二个单词: "tea" --> "aet"

"aet" 存在于 map, 加入其 Values {"aet" : [ "eat" , "tea" ] }

第三个单词: "tan" --> "ant"

"ant" 不存在于 map, 建立映射 {"aet" : [ "eat" , "tea" ] ; "ant" : [ "tan" ] }

第四个单词: "ate" --> "aet"

"aet" 存在于 map, 加入其 Values {"aet" : [ "eat" , "tea" , "ate" ] ; "ant" : [ "tan" ] }

......

map = {"aet" : [ "eat" , "tea" , "ate" ] ; "ant" : [ "tan" , "nat"] ; "abt" : [ "bat" ] }

返回该哈希映射的 Values 组成的数组:

[

["ate","eat","tea"],

["nat","tan"],

["bat"]

]

复杂度:

时间复杂度:O(N*(K*logK)),其中 N 是 strs 的长度,而 K 是 strs 中字符串的最大长度。遍历每个字符串时复杂度为 O(N)。使用内置排序函数排序字符串中的字母的时间复杂度为 O(K*logK)。

空间复杂度:O(N*K),存储在 map 中数据所占用的空间。

统计字频解题:

这种解题方法还可以再优化, 可以省略对字符串排序的操作。

仔细想想,一个单词最多由 26 个英文字母组成, 不就也可以建立一个哈希映射吗? 如:

对于单词 "aeat" :

建立哈希映射{ 'a' : 2 ; 'e' : 1; t : 1 }

key 为出现的单词, value 出现的频次。如果遍历每个 key 判断字母是否相等, 再判断出现次数是否相等, 这显然是更复杂了。

可以将每个字母出现的频次组成连续字符:

每个字母 a-z 出现频次: [2,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0]

组成字符串: "20001000000000000001000000"

只需判断每个单词的字母频次字符串是否相同就可以了。

对于求词频还可以优化, 字母数量固定 26 个, 直接建立一个长度为 26 的数组, 其索引代表二十六个字母位, 遍历单词中的字母, 字母每出现一次, 数组中代表该字母的元素值加 1。

这样就避免了排序操作

排序字母解题:

Java:

class Solution {

public List> groupAnagrams(String[] strs) {

if(strs.length==0) return new ArrayList<>();

Map> map = new HashMap<>();//建立映射关系

for (String s : strs) {//遍历该字符串数组

char[] chs = s.toCharArray();//转成字符

Arrays.sort(chs);//排序字符串字母

String key = String.valueOf(chs);//转成字符串

if(!map.containsKey(key)) map.put(key, new ArrayList<>());//如果 key 不存在, 新建映射关系

map.get(key).add(s);//加入其对应的 Value 所在的数组

}

return new ArrayList(map.values());//返回 Values 组成的数组

}

}

Python:

class Solution:

def groupAnagrams(self, strs: List[str]) -> List[List[str]]:

ans = collections.defaultdict(list) # 建立映射关系

for s in strs: # 遍历该字符串数组

ans[tuple(sorted(s))].append(s) # sorted(s):排序字符串字母, 并加入其对应的 Value 所在的数组

return ans.values() # 返回 Values 组成的数组

统计字频解题:

Java:

class Solution {

public List> groupAnagrams(String[] strs) {

if (strs.length == 0) return new ArrayList<>();

Map> map = new HashMap<>();// 建立映射关系

for (String s : strs) {//遍历该字符串数组

int[] count = new int[26];//建立一个 26 字母的映射关系

for (char c : s.toCharArray()) count[c - 'a']++;//遍历字字符串每个字母统计每个字母出现的频次

String key = Arrays.toString(count);//转成字符串

if (!map.containsKey(key)) map.put(key, new ArrayList<>());//如果 key 不存在, 新建映射关系

map.get(key).add(s);//加入其对应的 Value 所在的数组

}

return new ArrayList(map.values());//返回 Values 组成的数组

}

}

Python:

class Solution:

def groupAnagrams(self, strs: List[str]) -> List[List[str]]:

ans = collections.defaultdict(list)# 建立映射关系

for s in strs: # 遍历该字符串数组

count = [0] * 26 # 建立一个 26 字母的映射关系

for c in s: # 遍历字字符串每个字母

count[ord(c) - 97] += 1 # 每个字母出现的频次(元素值)加1

ans[tuple(count)].append(s) # 加入其对应的 Value 所在的数组

return ans.values() # 返回 Values 组成的数组

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
KMP算法是一种字符串匹配算法,用于在一个文本串S内查找一个模式串P的出现位置。它的时间复杂度为O(n+m),其中n为文本串的长度,m为模式串的长度。 KMP算法的核心思想是利用已知信息来避免不必要的字符比较。具体来说,它维护一个next数组,其中next[i]表示当第i个字符匹配失败时,下一次匹配应该从模式串的第next[i]个字符开始。 我们可以通过一个简单的例子来理解KMP算法的思想。假设文本串为S="ababababca",模式串为P="abababca",我们想要在S中查找P的出现位置。 首先,我们可以将P的每个前缀和后缀进行比较,得到next数组: | i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | | --- | - | - | - | - | - | - | - | - | | P | a | b | a | b | a | b | c | a | | next| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 接下来,我们从S的第一个字符开始匹配P。当S的第七个字符和P的第七个字符匹配失败时,我们可以利用next[6]=4,将P向右移动4个字符,使得P的第五个字符与S的第七个字符对齐。此时,我们可以发现P的前五个字符和S的前五个字符已经匹配成功了。因此,我们可以继续从S的第六个字符开始匹配P。 当S的第十个字符和P的第八个字符匹配失败时,我们可以利用next[7]=1,将P向右移动一个字符,使得P的第一个字符和S的第十个字符对齐。此时,我们可以发现P的前一个字符和S的第十个字符已经匹配成功了。因此,我们可以继续从S的第十一个字符开始匹配P。 最终,我们可以发现P出现在S的第二个位置。 下面是KMP算法的C++代码实现:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值