leetcode
leetcode 2501 ~ 2550
消除相邻近似相等字符

消除相邻近似相等字符

难度:

标签:

题目描述

You are given a 0-indexed string word.

In one operation, you can pick any index i of word and change word[i] to any lowercase English letter.

Return the minimum number of operations needed to remove all adjacent almost-equal characters from word.

Two characters a and b are almost-equal if a == b or a and b are adjacent in the alphabet.

 

Example 1:

Input: word = "aaaaa"
Output: 2
Explanation: We can change word into "acaca" which does not have any adjacent almost-equal characters.
It can be shown that the minimum number of operations needed to remove all adjacent almost-equal characters from word is 2.

Example 2:

Input: word = "abddez"
Output: 2
Explanation: We can change word into "ybdoez" which does not have any adjacent almost-equal characters.
It can be shown that the minimum number of operations needed to remove all adjacent almost-equal characters from word is 2.

Example 3:

Input: word = "zyxyxyz"
Output: 3
Explanation: We can change word into "zaxaxaz" which does not have any adjacent almost-equal characters. 
It can be shown that the minimum number of operations needed to remove all adjacent almost-equal characters from word is 3.

 

Constraints:

  • 1 <= word.length <= 100
  • word consists only of lowercase English letters.

代码结果

运行时间: 27 ms, 内存: 16.1 MB


/*
题目思路:
1. 使用Java Stream遍历字符串中的字符。
2. 利用Stream的特性进行过滤和映射,将近似相等的字符修改。
3. 计算并返回最小操作次数。
*/

import java.util.stream.IntStream;

public class Solution {
    public int minOperations(String word) {
        int[] operations = {0};
        StringBuilder sb = new StringBuilder(word);
        IntStream.range(0, word.length() - 1)
                .forEach(i -> {
                    if (areSimilar(sb.charAt(i), sb.charAt(i + 1))) {
                        operations[0]++;
                        sb.setCharAt(i + 1, getReplacementChar(sb.charAt(i), sb.charAt(i + 1)));
                    }
                });
        return operations[0];
    }

    private boolean areSimilar(char a, char b) {
        return a == b || Math.abs(a - b) == 1;
    }

    private char getReplacementChar(char a, char b) {
        return IntStream.range('a', 'z' + 1)
                .filter(c -> c != a && c != b && Math.abs(c - a) > 1 && Math.abs(c - b) > 1)
                .mapToObj(c -> (char) c)
                .findFirst().orElse('a');
    }
}

解释

方法:

此题解的思路基于贪心算法,目的是通过最少的修改次数去除字符串中所有相邻的近似相等字符。定义两个字符为近似相等,如果它们是相同的或者在英文字母表中相邻。解决方案从字符串的第二个字符开始遍历,比较每个字符与它前一个字符的ASCII码差的绝对值。如果这个差的绝对值小于等于1,说明这两个字符是近似相等的,需要将其中一个字符修改为一个不同且不相邻的字符,然后跳过下一个字符(因为已经修改了当前字符,所以跳过下一个字符以避免重复处理)。如果不是近似相等,则继续检查下一对字符。通过这种方式,逐对检查并修改字符,直到字符串末尾。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在贪心算法中,为什么选择跳过下一个字符是合理的处理方式?是否有可能因为跳过而错过了需要修改的相邻字符对?
在贪心算法中,跳过下一个字符是基于一个假设:当修改一个字符以消除其与前一个字符的近似相等关系时,该字符与后一个字符之间不会形成新的近似相等关系。这种处理方式简化了问题,减少了修改次数。然而,这个假设不总是正确,确实存在因跳过而错过需要修改的相邻字符对的情况。这可能导致算法没有达到理想的最优解,但仍然可以提供一个近似解。在实际应用中,这种方法的效率和实现的简单性是其主要优点。
🦆
当修改一个字符以消除近似相等时,如何选择一个合适的替换字符以确保最大程度地减少总的操作次数?
选择一个合适的替换字符时,应避免选择与当前字符相同或相邻的字符,同时也需要考虑新字符不会与前一个或后一个字符形成新的近似相等关系。理想情况下,可以选择字母表中离当前字符最远的字符,例如将 'a' 或 'b' 替换为 'z',或相反。这样的选择可以最大程度地减少后续可能需要的修改次数。然而,选择最佳替换字符可能需要根据上下文动态决定,以适应不同的输入字符串。
🦆
此题解中提到使用ASCII码差来判断是否近似相等,是否存在一种情况下,字符的ASCII码差值不大于1但字符并非相邻字母?
在ASCII码表中,相邻的英文字母(如 'a' 和 'b','b' 和 'c')的ASCII码值相差确实为1。然而,对于非字母字符或大小写不同的英文字母(如 'a' 和 'A'),ASCII码差值可能为1或更小,但它们并非相邻字母。因此,如果字符串包含大小写字母或非字母字符,仅使用ASCII码差来判断可能不准确。应考虑扩展判断逻辑,仅对小写或大写英文字母应用ASCII差值判断。
🦆
代码中假设了所有字符都需要修改,但实际上可能不是每个字符都需要修改。这种处理方式是否最优?
代码实现假设一旦发现近似相等的字符对,就必须修改其中一个字符,并跳过下一个字符。这种方法虽然简单,但并非总是最优。在某些情况下,可能存在不需要修改的字符对,或者通过修改不同的字符可以达到更少的总修改次数。一个更优的方法可能包括更复杂的逻辑,如动态规划,考虑多个字符的相互关系,以寻求全局最小的修改次数。然而,这将增加实现的复杂性。

相关问题