【LeetCode - 527】单词缩写

1、题目描述

在这里插入图片描述

2、解题思路

  给定的单词数组肯定会出现一些单词具有一样的缩写,比如给定的单词为{“abaaaaaf”, “abcaaaaf”, “abcdaaaf”, “abcdeaaf”}

  这些单词按照“始字母+省略字母的数量+结尾字母”结果都为 “a6f”。

  按照题意来说,如果存在冲突,则使用更长的前缀代替首字母,直到单词到缩写的映射唯一,也就是说 “a5f” 谁都别想用了,全换新的缩写。

  缩写结果为:{“aba4f”, “abca3f”, “abcda2f”, “abcde2f”}

  怎么计算得到呢?

  首先对有冲突的单词进行字典序排序,上面的例子已经是字典序。

  1、看第一个字符串和第二个字符串:abaaaaaf 和 abcaaaaf。

  前面的 “ab” 两个都有,因此两个单词都不能用 “ab” 做前缀,于是 abaaaaaf 的前缀必须用更长一位,前缀变为 “aba”,接着就是长度加末尾,变为 “aba4f”。

  2、接着看第二个字符串和第三个字符串,abcaaaaf 和 abcdaaaf。

  前面的 “abc” 两个都有,因此两个单词都不能用 “abc” 做前缀,于是 abcaaaaf 的前缀必须用更长一位,变为 “abca”,于是缩写变为 “abca3f”。

  3、以此类推,直到最后一个单词 abcdeaaf,它只有一个限制,不能用 “abcd” 作为前缀,因此前缀变为 “abcde”,于是缩写变为 “abcde2f”

  总结上述规律,先对有相同原始缩写的单词进行字典序排序。

  每一个单词都和它邻居的单词存在公共前缀,这个前缀的长度至少为 1。

  比如上面例子的 “abcaaaaf”,它和前一个的公共前缀是 “ab”,和后一个的公共前缀是 “abc”,于是 “abcaaaaf” 的缩写这两个前缀都不可用,必须使用更长的前缀,这就是为什么 “abcaaaaf” 的缩写前缀最终确定为 “abca”。

  按照这个规则,每一个单词都会有独一无二的缩写。

3、解题代码

class Solution {
    public List<String> wordsAbbreviation(List<String> words) {
        // 相同首字母、尾字母和长度的分在一个组
        // key 为缩写单词,value 为缩写是 key 的字符串在 words 中的索引
        Map<String, List<IndexedWord>> groups = new HashMap();
        String[] ans = new String[words.size()];
        int index = 0;
        // 遍历 words 进行分组
        for (String word : words) {
            // ab 是 word 按照 首字符+长度+尾字符 形式进行缩写的结果
            String ab = abbrev(word, 0);
            if (!groups.containsKey(ab)) {
                groups.put(ab, new ArrayList());
            }
            // index 为单词 word 原本在 words 中的索引
            // 把缩写一样的单词分在一组中
            groups.get(ab).add(new IndexedWord(word, index));
            index++;
        }
        // 遍历每一个组
        for (List<IndexedWord> group : groups.values()) {
            // group 中保存的是所有缩写一样的原单词和它们原来的索引
            // 对组内单词进行排序,字典序
            group.sort(Comparator.comparing(a -> a.word));
            // 存放每个组的最长公共前缀的长度
            int[] lcp = new int[group.size()];
            for (int i = 1; i < group.size(); ++i) {
                // 计算两个单词的最长公共前缀长度
                int p = longestCommonPrefix(group.get(i - 1).word, group.get(i).word);
                // lcp[i] = p 表示第 i 个单词的 0 到 p-1 是和上一个单词的公共前缀
                lcp[i] = p;
                lcp[i - 1] = Math.max(lcp[i - 1], p);
            }
            // 按照最初的位置关系放好
            for (int i = 0; i < group.size(); ++i) {
                ans[group.get(i).index] = abbrev(group.get(i).word, lcp[i]);
            }
        }
        return Arrays.asList(ans);
    }

    /**
     * word 的缩写形式
     *
     * @param word 待缩写的单词
     * @param i    0 到 i 作为前缀,最后一个字符做后缀
     * @return 缩写结果
     */
    public String abbrev(String word, int i) {
        int N = word.length();
        if (N - i <= 3) return word;    // 长度为 3 不用缩写
        // 缩写,首字符 + 长度 + 尾字符
        return word.substring(0, i + 1) + (N - i - 2) + word.charAt(N - 1);
    }

    /**
     * 计算两个单词的最长公共前缀长度
     *
     * @param word1
     * @param word2
     * @return
     */
    public int longestCommonPrefix(String word1, String word2) {
        int i = 0;
        while (i < word1.length() && i < word2.length() &&
                word1.charAt(i) == word2.charAt(i)) {
            i++;
        }
        return i;
    }

    class IndexedWord {
        String word;
        int index;

        IndexedWord(String w, int i) {
            word = w;
            index = i;
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值