单词的压缩编码

附上一道shell编程,关于识别有效电话号码。解题思路很简单,使用正则即可。

  • 题目描述:给定一个包含电话号码列表(一行一个电话号码)的文本文件 file.txt,写一个 bash 脚本输出所有有效的电话号码。

你可以假设一个有效的电话号码必须满足以下两种格式: (xxx) xxx-xxxx 或 xxx-xxx-xxxx。(x 表示一个数字)

你也可以假设每行前后没有多余的空格字符。

示例:
假设 file.txt 内容如下:
987-123-4567
123 456 7890
(123) 456-7890
你的脚本应当输出下列有效的电话号码:
987-123-4567
(123) 456-7890
链接:https://leetcode-cn.com/problems/valid-phone-numbers

  • 题解:


    正则示意图

    最终表达式如下:
    ^([0-9]{3}-|([0-9]{3}) )[0-9]{3}-[0-9]{4}$

  • shell命令:
    grep与awk
    grep
    grep -P '^([0-9]{3}-|([0-9]{3}) )[0-9]{3}-[0-9]{4}' file.txt awk/gawk awk '/^([0-9]{3}-|\([0-9]{3}\) )[0-9]{3}-[0-9]{4}/' file.txt
    或者
    gawk '/^([0-9]{3}-|([0-9]{3}) )[0-9]{3}-[0-9]{4}$/' file.txt

  • 附加快速查看表
    为了方便查看,列出对应的特殊字符表以及表达方式
    特殊字符 表达

  • 特殊字符 转义表达 特殊含义
    () () 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用
    \ 匹配输入字符串的结尾位置

  • * 匹配前面的子表达式零次或多次
  • + 匹配前面的子表达式一次或多次
    . . 匹配除换行符 \n 之外的任何单字符
    [ ] [] 标记一个中括号表达式的开始。要匹配 [,请使用 [。
    ? ? 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符
    \ \ 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符
    ^ ^ 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合
    {} {} 标记限定符表达式的开始
    | | 指明两项之间的一个选择
    Note 表含义中的出现次数:限定符前面字符的出现次数。
  • 限定符 表达含义
  • 出现次数>=0
  • 出现次数>=1
    ? 出现次数 0 or 1, 等价{0,1}
    {n} 出现次数=n
    {n,} 出现次数>=n
    {n, m} n=< 出现次数<= m

今日份的算法题如下:

  • 题目描述:给定一个单词列表,我们将这个列表编码成一个索引字符串 S 与一个索引列表 A。

例如,如果这个列表是 ["time", "me", "bell"],我们就可以将其表示为 S = "time#bell#" 和 indexes = [0, 2, 5]。

对于每一个索引,我们可以通过从字符串 S 中索引的位置开始读取字符串,直到 "#" 结束,来恢复我们之前的单词列表。

那么成功对给定单词列表进行编码的最小字符串长度是多少呢?

示例:
输入: words = ["time", "me", "bell"]
输出: 10
说明: S = "time#bell#" , indexes = [0, 2, 5] 。

提示:
1 <= words.length <= 2000
1 <= words[i].length <= 7
每个单词都是小写字母 。
链接:https://leetcode-cn.com/problems/short-encoding-of-words

  • 解题思路1:

题目要求输入一组字符串列表,输出最小编码后的字符串长度;
如列表中有重复字符串,需合并;
如列表中存在一个字符串是另一个的后缀,需合并;
由于题目对列表和字符串长度均有限制,则可以采用遍历的粗暴方法。

  • Python版:
class Solution:
    def minimumLengthEncoding(self, words: List[str]) -> int:
        good = set(words)
        for word in words:
            for k in range(1, len(word)):
                good.discard(word[k:])

        return sum(len(word) + 1 for word in good)

Tips:

  • 使用python的集合可以方便地去重

  • 分别遍历字符串和字符串中的每个字符,用切片的方式比较

  • set.discard删除不存在的不会报错,而remove删除不存在会报错的

  • 由于set(words) = k为0时的情况,所以最后k从1开始遍历即可

  • 最后sum函数中对每个字符串都加1表示‘#’的长度

  • C++版:

class Solution {
public:
    int minimumLengthEncoding(vector<string>& words) {
        unordered_set<string> good(words.begin(), words.end());
        for (const string& word: words) {
            for (int k = 1; k < word.size(); ++k) {
                good.erase(word.substr(k));
            }
        }

        int ans = 0;
        for (const string& word: good) {
            ans += word.size() + 1;
        }
        return ans;
    }
};

Tips:

  • C++的vector(向量)容器是封装了动态大小数组的顺序容器,能存放任意类型的动态数组

  • C++中指定字符串长度的方法为word.size(),Java是word.length(),python是len(word)

  • 使用unordered_set(C++11中的新特性),基于哈希表。C++ 11中对unordered_set描述大体如下:无序集合容器(unordered_set)是一个存储唯一(unique,即无重复)的关联容器(Associative container),容器中的元素无特别的秩序关系,该容器允许基于值的快速元素检索,同时也支持正向迭代。在一个unordered_set内部,元素不会按任何顺序排序,而是通过元素值的hash值将元素分组放置到各个槽(Bucker,也可以译为“桶”),这样就能通过元素值快速访问各个对应的元素(均摊耗时为O(1))。
    原型中的Key代表要存储的类型,而hash<Key>也就是你的hash函数,equal_to<Key>用来判断两个元素是否相等,allocator<Key>是内存的分配策略。一般情况下,我们只关心hash<Key>和equal_to<Key>参数,下面将介绍这两部分。(参考原文链接:https://blog.csdn.net/vevenlcf/article/details/51743058

  • C++直接提供了substr函数,需要两个参数,即字符串的begin和end,可以很快地识别出字符串后缀;

  • erase是容器中定义的擦除函数:
    std::vector::erase()
     函数原型:iterator erase (iterator position);  //删除指定元素
    iterator erase (iterator first, iterator last);  //删除指定范围内的元素
    返回值:指向删除元素(或范围)的下一个元素。

  • Java版:

class Solution {
    public int minimumLengthEncoding(String[] words) {
        Set<String> good = new HashSet(Arrays.asList(words));
        for (String word: words) {
            for (int k = 1; k < word.length(); ++k)
                good.remove(word.substring(k));
        }

        int ans = 0;
        for (String word: good)
            ans += word.length() + 1;
        return ans;
    }
}

Tips:

  • 使用了Array.asList()方法,目的是将一个变长参数或数组转换为List
  • 使用HashSet()方法,构造一个新的空set,remove()是HashSet的常用方法;C++中的substr()方法在Java中变为substring()

  • 解题思路2:利用Trie树。反序将字符元素插入Trie树中,保留根节点为空,最后统计叶子节点数+1即为答案。


    Trie树构造
  • Python版

class Solution:
    def minimumLengthEncoding(self, words: List[str]) -> int:
        words = list(set(words)) #remove duplicates
        #Trie is a nested dictionary with nodes created
        # when fetched entries are missing
        Trie = lambda: collections.defaultdict(Trie)
        trie = Trie()

        #reduce(..., S, trie) is trie[S[0]][S[1]][S[2]][...][S[S.length - 1]]
        nodes = [reduce(dict.__getitem__, word[::-1], trie)
                 for word in words]

        #Add word to the answer if it's node has no neighbors
        return sum(len(word) + 1
                   for i, word in enumerate(words)
                   if len(nodes[i]) == 0)

Tips:

  • 利用Python写trie树真的很简洁...
  • 在计算node时,用到了特殊方法getitem。凡是在类中定义了这个getitem 方法,那么它的实例对象(假定为p),可以像这样
    p[key] 取值,当实例对象做p[key] 运算时,会调用类中的方法getitem
    一般如果想使用索引访问元素时,就可以在类中定义这个方法(getitem(self, key) )。 参考python魔方方法
  • reduce()函数。reduce() 函数会对参数序列中元素进行累积。函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。
    语法
    • reduce() 函数语法:reduce(function, iterable[, initializer])
    • 参数
      function -- 函数,有两个参数
      iterable -- 可迭代对象
      initializer -- 可选,初始参数
    • 例子:reduce(lambda x, y: x+y, [1,2,3,4,5]) # 使用 lambda 匿名函数
      输出:15
  • 先构造了trie字典,key为逆序排列的words列表中的每个字符,调用reduce函数进行累积计算,计算结果若不为0,则是后缀词。
  • 当word = 'time'时,reduce返回结果:


    非后缀词
  • 当word = 'me' 时,reduce返回结果:


    后缀词
  • 因为pythonic风格对此题过于明显,就不放上C++和Java版本了...
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355