LeetCode 2273. 移除字母异位词后的结果数组【原地做法+字符串+哈希表或排序】

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章中,我不仅会讲解多种解题思路及其优化,还会用多种编程语言实现题解,涉及到通用解法时更将归纳总结出相应的算法模板。

为了方便在PC上运行调试、分享代码文件,我还建立了相关的仓库:https://github.com/memcpy0/LeetCode-Conquest。在这一仓库中,你不仅可以看到LeetCode原题链接、题解代码、题解文章链接、同类题目归纳、通用解法总结等,还可以看到原题出现频率和相关企业等重要信息。如果有其他优选题解,还可以一同分享给他人。

由于本系列文章的内容随时可能发生更新变动,欢迎关注和收藏征服LeetCode系列文章目录一文以作备忘。

给你一个下标从 0 开始的字符串 words ,其中 words[i] 由小写英文字符组成。

在一步操作中,需要选出任一下标 i ,从 words 中 删除 words[i] 。其中下标 i 需要同时满足下述两个条件:

  1. 0 < i < words.length
  2. words[i - 1] 和 words[i] 是 字母异位词 。

只要可以选出满足条件的下标,就一直执行这个操作。

在执行所有操作后,返回 words 。可以证明,按任意顺序为每步操作选择下标都会得到相同的结果。

字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。例如,"dacb" 是 "abdc" 的一个字母异位词。

示例 1:

输入:words = ["abba","baba","bbaa","cd","cd"]
输出:["abba","cd"]
解释:
获取结果数组的方法之一是执行下述步骤:
- 由于 words[2] = "bbaa" 和 words[1] = "baba" 是字母异位词,选择下标 2 并删除 words[2] 。
  现在 words = ["abba","baba","cd","cd"]- 由于 words[1] = "baba" 和 words[0] = "abba" 是字母异位词,选择下标 1 并删除 words[1] 。
  现在 words = ["abba","cd","cd"]- 由于 words[2] = "cd" 和 words[1] = "cd" 是字母异位词,选择下标 2 并删除 words[2] 。
  现在 words = ["abba","cd"] 。
无法再执行任何操作,所以 ["abba","cd"] 是最终答案。

示例 2:

输入:words = ["a","b","c","d","e"]
输出:["a","b","c","d","e"]
解释:
words 中不存在互为字母异位词的两个相邻字符串,所以无需执行任何操作。

提示:

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 10
  • words[i] 由小写英文字母组成

方法 原地做法+字符串+哈希表/排序

类似26. 删除有序数组中的重复项,连续相同的字母异位词,只保留其中最左边的。

示例 1 的 a b b a , b a b a , b b a a abba,baba,bbaa abba,baba,bbaa 互为字母异位词,保留最左边的 a b b a abba abba c d , c d cd,cd cd,cd 互为字母异位词,保留最左边的 c d cd cd

根据题意,如果两个字符串每种字母的出现次数相同,就是一对字母异位词。可以用哈希表统计字母出现次数,也可以把字符串排序,两个排序后相同的字符串是一对字母异位词。其余逻辑同 26 题。

class Solution {
public:
    vector<string> removeAnagrams(vector<string>& words) {
        string base = "";
        int k = 0;
        for (auto& word : words) {
            string s = word;
            ranges::sort(s);
            if (s != base) {
                base = move(s);
                words[k++] = word; // 保留 word
            }
        }
        words.resize(k);
        return words;
    }
};
class Solution {
    public List<String> removeAnagrams(String[] words) {
        char[] base = words[0].toCharArray();
        Arrays.sort(base);
        int k = 1;
        for (int i = 1; i < words.length; i++) {
            char[] s = words[i].toCharArray();
            Arrays.sort(s);
            if (!Arrays.equals(s, base)) {
                base = s;
                words[k++] = words[i]; // 保留 words[i]
            }
        }
        return Arrays.asList(Arrays.copyOf(words, k));
    }
}
impl Solution {
    pub fn remove_anagrams(mut words: Vec<String>) -> Vec<String> {
        let mut base = vec![];
        let mut k = 0;
        for i in 0..words.len() {
            let mut s = words[i].as_bytes().to_vec();
            s.sort_unstable();
            if s != base {
                base = s;
                words[k] = words[i].clone(); // 保留 words[i]
                k += 1;
            }
        }
        words.truncate(k);
        words
    }
}
func removeAnagrams(words []string) []string {
	var base []byte
	k := 0
	for _, word := range words {
		s := []byte(word)
		slices.Sort(s)
		if !bytes.Equal(s, base) {
			base = s
			words[k] = word // 保留 word
			k++
		}
	}
	return words[:k]
}
class Solution:
    def removeAnagrams(self, words: List[str]) -> List[str]:
        k = 1
        for s, t in pairwise(words):
            if sorted(s) != sorted(t):
                words[k] = t
                k += 1
        del words[k:]
        return words
# 写法二
class Solution:
    def removeAnagrams(self, words: List[str]) -> List[str]:
        k = 1
        for s, t in pairwise(words):
            if Counter(s) != Counter(t):
                words[k] = t
                k += 1
        del words[k:]
        return words
int cmp(const void* a, const void* b) {
    return (*(char*)a - *(char*)b);
}

char ** removeAnagrams(char ** words, int wordsSize, int* returnSize){
    char* base = strdup(words[0]);
    qsort(base, strlen(base), sizeof(char), cmp);
    int k = 1;
    for (int i = 1; i < wordsSize; i++) {
        char* s = strdup(words[i]);
        qsort(s, strlen(s), sizeof(char), cmp);
        if (strcmp(s, base)) {
            free(base);
            base = s;
            words[k++] = words[i]; // 保留 words[i]
        } else {
            free(s);
        }
    }
    free(base);
    *returnSize = k;
    return words;
}
var removeAnagrams = function(words) {
    let base = '';
    let k = 0;
    for (const word of words) {
        const s = word.split('').sort().join('');
        if (s !== base) {
            base = s;
            words[k++] = word; // 保留 word
        }
    }
    words.length = k;
    return words;
};

复杂度分析:

  • 时间复杂度: O ( n L ) O(nL) O(nL) O ( n L log ⁡ L ) O(nL \log L) O(nLlogL) ,其中 n n n w o r d s words words 的长度, L ≤ 10 L\le 10 L10 w o r d s [ i ] words[i] words[i] 的最大长度。
  • 空间复杂度: O ( L ) O(L) O(L)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

memcpy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值