leetcode
leetcode 351 ~ 400
最短独占单词缩写

最短独占单词缩写

难度:

标签:

题目描述

代码结果

运行时间: 68 ms, 内存: 16.3 MB


/*
 * 题目思路:
 * 使用Java Stream API生成target的所有可能缩写,然后找到字典中不存在的最短缩写。
 */
 
import java.util.*;
import java.util.stream.*;
 
public class Solution {
    public String minAbbreviation(String target, String[] dictionary) {
        List<String> abbrs = generateAbbreviations(target).stream()
                .sorted(Comparator.comparingInt(String::length))
                .collect(Collectors.toList());
        return abbrs.stream()
                .filter(abbr -> !isConflict(abbr, dictionary))
                .findFirst()
                .orElse("");
    }
 
    private List<String> generateAbbreviations(String word) {
        List<String> result = new ArrayList<>();
        generateAbbreviations(word, 0, new StringBuilder(), 0, result);
        return result;
    }
 
    private void generateAbbreviations(String word, int pos, StringBuilder cur, int count, List<String> result) {
        int len = cur.length();
        if (pos == word.length()) {
            if (count > 0) cur.append(count);
            result.add(cur.toString());
        } else {
            generateAbbreviations(word, pos + 1, cur, count + 1, result);
            if (count > 0) cur.append(count);
            cur.append(word.charAt(pos));
            generateAbbreviations(word, pos + 1, cur, 0, result);
        }
        cur.setLength(len);
    }
 
    private boolean isConflict(String abbr, String[] dictionary) {
        return Arrays.stream(dictionary).anyMatch(word -> match(word, abbr));
    }
 
    private boolean match(String word, String abbr) {
        int m = word.length(), n = abbr.length();
        int i = 0, j = 0;
        while (i < m && j < n) {
            if (Character.isDigit(abbr.charAt(j))) {
                if (abbr.charAt(j) == '0') return false;
                int start = j;
                while (j < n && Character.isDigit(abbr.charAt(j))) j++;
                int num = Integer.parseInt(abbr.substring(start, j));
                i += num;
            } else {
                if (word.charAt(i++) != abbr.charAt(j++)) return false;
            }
        }
        return i == m && j == n;
    }
}
 

解释

方法:

该题解使用位掩码来枚举target字符串的所有可能的缩写形式。对于字典中的每个单词,计算它与target的位掩码差异,并将差异掩码存储在一个集合中。然后枚举target的所有可能的位掩码,检查每个掩码是否能够区分target和字典中的所有单词。在所有有效的掩码中,选择对应的缩写长度最短的那个作为最终答案。

时间复杂度:

O(mn + m2^n)

空间复杂度:

O(m + n)

代码细节讲解

🦆
题解中提到使用位掩码来枚举target字符串的所有可能缩写形式,这种方法在实际编程中是如何实现的?能否具体解释一下位掩码是如何与target字符串的缩写形式对应的?
在题解中,位掩码是一种技巧,用来表示字符串的每个字符是否被省略或保留。每个位(0或1)代表一个字符:'1'表示保留字符,'0'表示省略字符。例如,对于字符串'target',掩码'101010'(二进制表示)意味着保留第1、3、5个字符,省略第2、4、6个字符,从而形成缩写。枚举所有可能的位掩码等同于枚举target的所有可能缩写形式。通过对0到2^len(target)-1的所有整数进行循环,我们可以生成所有可能的掩码,并用这些掩码来构建target的每一种缩写形式。
🦆
在检查每个掩码是否能区分target和字典中所有单词时,条件`i & mask > 0`是如何确保掩码能有效区分不同单词的?是否存在更简单或更直观的方式来理解这一逻辑?
条件`i & mask > 0`确保掩码`i`在与字典中每个单词对应的掩码`mask`进行按位与操作后,结果不为零。这意味着至少存在一个位置,在该位置上,target和字典中的单词在掩码`i`中是被保留的(即对应位是1),而在单词掩码`mask`中是不同的(单词在该位置的字符与target不同)。这保证了掩码`i`能够区分target和字典中的该单词。更直观的理解是:如果掩码`i`至少在一个与字典单词不同的字符位置上为1,那么这个掩码就能帮助区分target和该字典单词。
🦆
函数`getAbbr`中的变量`prev`的作用是什么?如何通过这个变量计算缩写中数字的位置和数目?
在`getAbbr`函数中,`prev`变量用来记录上一个被省略字符的位置。当遇到一个要保留的字符时(即掩码在该位置是1),如果`prev`不为空(即之前有省略的字符),则计算从`prev`位置到当前字符位置之间的字符数,这个数值将被添加到缩写中。例如,如果`prev`是2,当前位置是5,那么在缩写中添加'3'表示这两个位置之间省略了3个字符。此后,`prev`被重置为null,直到遇到下一个省略的字符序列,再次开始计数。如果`prev`非空且遍历完所有字符,还需要添加从`prev`到字符串末尾的省略字符数目。

相关问题

列举单词的全部缩写

列举单词的全部缩写

有效单词缩写

有效单词缩写

单词缩写

单词缩写