527. Word Abbreviation
阿新 • • 發佈:2018-11-08
Given an array of n distinct non-empty strings, you need to generate minimal possible abbreviations for every word following rules below. 1. Begin with the first character and then the number of characters abbreviated, which followed by the last character. 2. If there are any conflict, that is more than one words share the same abbreviation, a longer prefix is used instead of only the first character until making the map from word to abbreviation become unique. In other words, a finalabbreviation cannot map to more than one original words. 3. If the abbreviation doesn't make the word shorter, then keep it as original. Example: Input: ["like", "god", "internal", "me", "internet", "interval", "intension", "face", "intrusion"] Output: ["l2e","god","internal","me","i6t","interval","inte4n","f2e","intr4n"] https://www.youtube.com/watch?v=yAQMcGY4c90&t=22s https://leetcode.com/problems/word-abbreviation/solution/ Solution 1 : greedy Intuition Let's choose the shortest abbreviation for each word. Then, while we have duplicates, we'll increase the length of all duplicates. Algorithm For example, let's say we have "aabaaa", "aacaaa", "aacdaa", then we start with "a4a", "a4a", "a4a". Since these are duplicated, we lengthen them to "aa3a", "aa3a", "aa3a". They are still duplicated, so we lengthen them to "aab2a", "aac2a", "aac2a". The last two are still duplicated, so we lengthen them to "aacaaa", "aacdaa". Throughout thisprocess, we were tracking an index prefix[i] which told us up to what index to take the prefix to. For example, prefix[i] = 2 means to take a prefix of word[0], word[1], word[2]. 0 1. 2 3 4. 5. 6. 7 8 ["like", "god", "internal", "me", "internet", "interval", "intension", "face", "intrusion"] ans[] = l2e, god, i6l, me, i6t, i6l, I7n, f2e, I7n i = 2 Hashset For (j = 3; j < len, j++){ ans[j] = Set = (2, 5 ) ans[2] = abbr(dict.get(2), prefix index + 1) Input: ["like", "god", "internal", "me", "internet", "interval", "intension", "face", "intrusion"] Output: [“l2e","god","internal","me","i6t","interval","inte4n","f2e","intr4n"] // correct Make abbreviation for each word. Then, check each word, if there are some strings which have same abbreviation with it, increase the prefix. public List<String> wordsAbbreviation(List<String> dict) { int len=dict.size(); String[] ans=new String[len]; int[] prefix=new int[len]; for (int i=0;i<len;i++) { prefix[i]=1; ans[i]=makeAbbr(dict.get(i), 1); // make abbreviation for each string } for (int i=0;i<len;i++) { while (true) { HashSet<Integer> set=new HashSet<>(); for (int j=i+1;j<len;j++) { if (ans[j].equals(ans[i])) set.add(j); // check all strings with the same abbreviation } if (set.isEmpty()) break; set.add(i); for (int k: set) ans[k]=makeAbbr(dict.get(k), ++prefix[k]); // increase the prefix } } return Arrays.asList(ans); } private String makeAbbr(String s, int k) { if (k>=s.length()-2) return s; StringBuilder builder=new StringBuilder(); builder.append(s.substring(0, k)); builder.append(s.length()-1-k); builder.append(s.charAt(s.length()-1)); return builder.toString(); } // not tested class Solution { public List<String> wordsAbbreviation(List<String> dict) { int N = words.size(); String ans[] = new String[N]; int[] prefix = new int[N]; // abbrev all words for(int i = 0; i < N; i++){ ans[i] = abbr(words.get(i), 0); } for(int i = 0; i < N; i++){ while(true){ // the only condition to break the while (true) loop is when the set is empty // which means there is no duplicate abbr for this word at dict[i] HashSet<Integer> set = new HashSet<>(); for(int j = i + 1; j < N; j++){ if(ans[i].equals(ans[j])){ set.add(j); // add the dup 's index } } if(set.isEmpty()) break; // if no dup, move on to the next word // has duplicate set.add(i); // now we have collected all the duplicate abbr 's index in the dic // now it's time to extend the prefix for(int index : set){ ans[index] = abbr(dict.get(index), prefix[index]++); } } } return Arrays.asList(ans); // turn array into a list } private String abbr(String word, int start){ int n = word.length(); StringBuilder sb = new StringBuilder(); sb.append(word.substring(0, i + 1)).append(n - i - 2).append(word.charAt(n - 1)); return sb.toString(); // another way : do it with string + } } Solution 2 : group + trie 沒看懂 let's group words based on length, first letter, and last letter, and discuss when words in a group do not share a longest common prefix. Put the words of a group into a trie (prefix tree), and count at each node (representing some prefix P) the number of words with prefix P. If the count is 1, we know the prefix is unique.