算法:单词缩写

原题单词缩写

给出一组 n 个不同的非空字符串,您需要按以下规则为每个单词生成 最小 的缩写。

  • 从第一个字符开始,然后加上中间缩写掉的字符的长度,后跟最后一个字符。
  • 如果有冲突,就是多个单词共享相同的缩写,使用较长的前缀,而不是仅使用第一个字符,直到使单词的缩写的映射变为唯一。 换句话说,最终得到的缩写不能映射到多个原始单词。
  • 如果缩写不会使单词更短,则不进行缩写,保持原样。

审题

  1. 首先这题的有个地方说得不清楚,就是解决冲突的方式。比如iabcx和idefx冲突了,因为缩写都是i3x,这时需要把这两个都加长前缀,变成ia2x和id2x。重点在于两者都变化。应用到整个集合里,当有其他单词和你冲突时,你就要增大前缀,知道没有单词和你冲突。
  2. 我看了下题目的标签,里有个排序,顺着这个思路想了下:
  • 只有后缀相同的单词之前会可能冲突,因为后缀一定保留

  • 只有长度相同的会冲突。证明:如果长度不同的单词缩写到长度相同,那么缩写掉部分的长度肯定不同,那么中间的数字肯定不同,那么这两个缩写肯定不同,不会冲突。

  所以只有长度相同且后缀相同的单词才会冲突,所以把这些可能冲突的分到一个组里。

  1. 经过2的处理,同一个组里,都是有冲突隐患的单词。对于某一个单词,要保留多长的前缀才可以呢?它和组内的其他单词比较共同前缀,假如最长共同前缀为k,那么它就保留到k+1,因为到k+1的这一段,所有其他的都会跟它呈现出不同。如果直接照着这个直接实现,复杂度是O(n^2),n是组内的单词个数,有没有办法直接找到最长共同前缀呢?直接使用字符串的排序,字符串本身的比较方法是从前往后逐个比较字符,所以具有更多共同前缀的单词会贴到一起。
  2. 证明如下:

  假设排序后单词k和k-1的共同前缀长度是x,单词k和k+1的共同长度是y, 如果最长共同前缀不是x或y,那么存在一个单词t,它不在k的两边,且共同前缀z满足:z>x,z>y。

  单词k+1在k的后面,且共同前缀是y,说明:k+1[y+1]>k[y+1];同理对于k-1有:k-1[x+1]<k[x+1]。而z>x且z>y,那么这两个式子对于t也是成立的,也就是在排序后t会在k-1和k+1之间,而这时不可能的,所以出现错误假设不成立,不存在这样的t。

  简单说,公共前缀越长,排序后越靠近。所以每个单词只需要取左右相邻单词的共同前缀作为参考即可。

代码:

//对单词排序,按长度、后缀和单词本身的先后顺序
//这样长度相同、后缀相同的单词会分到一起
bool wordPairCmp(pair<string, int>& wp1, pair<string, int> &wp2){
    int result = (int)wp1.first.length()-(int)wp2.first.length();
    if (result!=0) return result<0;
    result = wp1.first.back()-wp2.first.back();
    if (result!=0) return result<0;
    
    return wp1.first.compare(wp2.first)<0;
}

//求共同前缀的长度
inline int prefixLapCount(string &s1, string &s2){
    int c = 0;
    while (s1[c] == s2[c]) {
        c++;
    }
    return c;
}

//把一个单词按指定前缀长度缩写
inline void wordAbb(string &originalWord, int prefixCount){
    int len = (int)originalWord.length();
    int cut = len-prefixCount-1;
    if (cut<=1) {
        return;
    }
    
    int destLen = prefixCount+1+log10(cut)+1;
    int preIdx = len-cut-2;
    string abb(destLen,' ');
    
    for (int i = 0; i<=preIdx; i++) {
        abb[i] = originalWord[i];
    }
    abb[destLen-1] = originalWord.back();
    
    //中间缩写的数字部分,没有使用atoi等方法而是直接实现,效率会快很对
    for (int i = destLen-2; i>preIdx; i--) {
        abb[i] = cut%10+'0';
        cut = cut/10;
    }
    
    originalWord = abb;
}

vector<string> wordsAbbreviation(vector<string> &dict) {
    
    //使用pair的原因是为了记录单词在原数组里的索引位置,这样排序后,还可以再重置到输入时的顺序
    vector<pair<string, int>> wordPairs;
    int i = 0;
    for (auto &w : dict){
        wordPairs.push_back({w,i});
        i++;
    }
    sort(wordPairs.begin(), wordPairs.end(), wordPairCmp);
    
    int size = (int)wordPairs.size();
    int preLapCount = 0; //和前一个重叠的字符个数
    for (int i = 0; i<size; i++) {
        
        int nextLapCount = 0;
        //因为没有使用分组,即没有用多维数组,而是单个数组,所以需要做前后判定,长度不同或者后缀不同,则共同前缀就不考虑了,直接是0.
        //这里会影响效率,因为大部分比较都是无意义的或者说跟排序的工作有重复
        if (i<size-1 &&
            wordPairs[i].first.length() == wordPairs[i+1].first.length() &&
            wordPairs[i].first.back() == wordPairs[i+1].first.back()) {
            nextLapCount = prefixLapCount(wordPairs[i].first, wordPairs[i+1].first);
        }
        
        wordAbb(wordPairs[i].first, max(preLapCount, nextLapCount)+1);
        preLapCount = nextLapCount;
    }
    
    for (auto &p : wordPairs){
        dict[p.second] = p.first;
    }
    
    return dict;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容

  • 前言 最先接触编程的知识是在大学里面,大学里面学了一些基础的知识,c语言,java语言,单片机的汇编语言等;大学毕...
    oceanfive阅读 3,044评论 0 7
  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,323评论 0 2
  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,367评论 0 5
  • 文|月半猫 每一个女生心中都有一个公主梦,希望自己的另一半能像王子一般,优秀、英俊,对爱情忠贞不渝。可现实生活中,...
    一条流浪的猫阅读 117评论 0 0
  • 有修养的人,既不乐观也不悲观,而是客观。他们不相信世界上有绝对三观相合、趣味相投的人,就像不相信有完全相同的两片树...
    嬉笑怒骂皆生活阅读 178评论 0 0