input:

["like","god","internal","me","internet","interval","incension","intension","face","intrusion"]

output:

["l2e","god","internal","me","i6t","interval","inc5n","inte4n","f2e","intr4n"]

 

规则:

1.头尾字符必须保留, 缩写的长度 和原来字符长度相等,则保留原字符串(字符串长度<=3时, 则没有缩写, 比如 god)

2. 缩写不能有冲突:例如 internal, interval

   可以缩写成 i6l, in5l, int4l, inte3l, inter2l  

    以及 intern1l, interv1l , 但这个和原字符长度相等,也就没必要缩写了,所以保留原字符串

3. incension-- inc5n,  intrusion , intension 变成 intr4n 和 inte4n 就不会冲突

方法一: 贪心法  复杂度 n^2

先把所有字符串变成最大程度的缩写:

l2e  god  i6l  me  i6t  i6l  i7n  i7n  f2e  i7n  

然后从index =0 开始 往 index+1 到 len 开始检查冲突。设计一个 pre[0-len-1] 来记录每个字符串前面字符串的个数, 理论上都应该初始化为1, 为了方便起见 在操作下标时, pre[i]+1 就OK了
从index =0 开始 选定一个 index, 然后while(true) 循环, 里面用一个set 记录冲突的index +j, 注意记录完 如果有冲突 需要把 index 本身也加入 set, 然后 pre[i]++ 去尝试一个更长的 缩写,直到冲突结束。

 1 class Solution {
 2     public List<String> wordsAbbreviation(List<String> dict) {
 3         
 4         int len = dict.size();
 5         int[] pre = new int[len]; 
 6         String[] ans = new String[len];
 7         for(int i=0; i<len; i++){
 8            ans[i] =  get_abbr(dict.get(i), pre[i]);
 9           // System.out.print(ans[i]+"  "); 
10         }
11         
12         for(int i=0; i<len-1; i++){
13             while(true){
14                 Set<Integer> set = new HashSet<>();
15                 for(int j= i+1; j<len; j++){
16                     if(ans[i].equals(ans[j])){
17                         set.add(j);
18                     }
19                 }
20                 if(set.isEmpty()) break;
21                 set.add(i);
22                 for(int index: set){
23                     ans[index] = get_abbr(dict.get(index),++pre[index]);
24                     System.out.print(ans[index]+"  ");  
25                 }
26                 System.out.println();  
27             }
28         }
29         return Arrays.asList(ans);
30     }
31     
32     String get_abbr(String word, int k){
33         int len = word.length();
34         if(len-k<=3) return word;
35         // use good as example,  g2d, 2 = n-(k+1)-1 = 4-1-1=2
36         return word.substring(0,k+1) + (len-k-2) + word.substring(len-1,len);
37     }
38 }
这是set 里 internal 和 interval 迭代的过程
in5l in5l
int4l int4l
inte3l inte3l
inter2l inter2l
internal interval

这是set里incension intrusion , intension 贪心法迭代的过程
in6n in6n in6n
inc5n int5n int5n
inte4n intr4n


方法二:group +least common prefix 分组+最小公共前缀 复杂度 nlogn

冲突的字符串一定满足 如下 特点: 第一个和最后一个字母一样,并且长度一样,例如
intrusion , intension ---> i7n
算法如下:
1. 求出每个字符串最短缩写,把相同的分组放在同一个 group里
2. 把每个分组排序,排序的目的为便于求出相邻之间最短公共前缀
为何要排序,如果不排序就得比较任意两个,复杂度为(n^2) ,排序后只用比较相邻两个,复杂度为 nlong+n
然而现实是 排序的时间复杂度 竟然比 不排序直接比较的复杂度高.

2.1 不排序直接暴力比较: 需要二重循环比较,得到 最长公共子串后取较大的那个
        for(int i=0; i<group.size() -1; i++){
                for(int j= i+1; j<group.size(); j++){
                   int p = longestCommonPrefix(group.get(i).word, group.get(j).word);
                     pre[i] = Math.max(pre[i], p);
                     pre[j] = Math.max(p,pre[j]);
                  // System.out.println(pre[i] +"  "+pre[j]);
              }

  2.2 先排序后 只用比较一轮的原理:  具有最长公共子川的两个string 排序后一定相邻。

            Collections.sort(group,(a,b)->(a.word.compareTo(b.word)));
             int[] pre = new int[group.size()];
             for(int i=1; i<group.size(); i++){
                pre[i] = longestCommonPrefix(group.get(i).word,group.get(i-1).word);
                pre[i-1] = Math.max(pre[i-1],pre[i]);
             }

 



3. 根据前缀长度重新去算不冲突的前缀

先排序再比较的版本: 
 1 class Solution {
 2     public List<String> wordsAbbreviation(List<String> dict) {
 3         
 4         int len = dict.size();
 5         
 6         String[] ans = new String[len];
 7         Map<String, List<IndexWord>> groups = new HashMap<>();
 8         
 9         for(int i=0; i<len; i++){  //先把string 分组放入 map
10            String abbr =  get_abbr(dict.get(i), 0);
11            if(!groups.containsKey(abbr)){
12                groups.put(abbr,new ArrayList<>());
13            } 
14            groups.get(abbr).add(new IndexWord(dict.get(i),i));           
15         }
16         
17         for(List<IndexWord> group: groups.values()){
18            // Collections.sort(group, (a, b) -> a.word.compareTo(b.word));  //按照字母排序
19             Collections.sort(group,(a,b)->(a.word.compareTo(b.word)));
20             int[] pre = new int[group.size()];
21             for(int i=1; i<group.size(); i++){
22                pre[i] = longestCommonPrefix(group.get(i).word,group.get(i-1).word);
23                pre[i-1] = Math.max(pre[i-1],pre[i]);
24             }
25             for(int i=0; i<group.size(); i++){
26                 ans[group.get(i).index] = get_abbr(group.get(i).word,pre[i]);
27             }
28         }
29 
30         return Arrays.asList(ans);
31     }
32     
33     int longestCommonPrefix(String word1, String word2){
34         int index =0;
35         while(index<word1.length() && index<word2.length() && word1.charAt(index) == word2.charAt(index)){
36             index ++;
37         }
38         return index;
39     }
40     
41     String get_abbr(String word, int k){
42         int len = word.length();
43         if(len-k<=3) return word;
44         // use good as example,  g2d, 2 = n-(k+1)-1 = 4-1-1=2
45         return word.substring(0,k+1) + (len-k-2) + word.substring(len-1,len);
46     }
47 }
48 
49 class IndexWord{
50     int index;
51     String word;
52     IndexWord(String word, int index){
53         this.word = word;
54         this.index = index;
55     }
56 }

不用排序,直接暴力比较的版本: 


 1  1         for(List<IndexWord> group: groups.values()){
 2  2 
 3  3             int[] pre = new int[group.size()];       
 4  4             for(int i=0; i<group.size() -1; i++){
 5  5                 for(int j= i+1; j<group.size(); j++){
 6  6                     int p = longestCommonPrefix(group.get(i).word, group.get(j).word);
 7  7                     pre[i] = Math.max(pre[i], p);
 8  8                     pre[j] = Math.max(p,pre[j]);
 9  9                    // System.out.println(pre[i] +"  "+pre[j]);
10 10                 }
11 11             }
12 12                 
13 13         

方法三: Tier Tree 




posted on 2018-11-13 13:55  KeepAC  阅读(446)  评论(0编辑  收藏  举报