3. Longest Substring Without Repeating Characters

Given a string, find the length of the longest substring without repeating characters.

Examples:

Given "abcabcbb", the answer is "abc", which the length is 3.

Given "bbbbb", the answer is "b", with the length of 1.

Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.

这道题求一个字符串的最长不重复子串,思路有三种:

  1. Brute force:计算每一个子串,然后判断其是否同时满足最长和无重复的条件,这个方法容易想到但一定会造成超时,因为时间复杂度是立方级的。
  2. hash+sliding window:首先,我们假设目前子串存在于{i,j}这个区间内,同时我们用哈希表来记录每一个字母的下标,假如此时下标为j+1的元素和前面的区间中某一个字符相同,即存在于哈希表之中,那么我们应该从下标为j+1的元素继续向后搜索,同时从哈希表中删除下标为i的元素;如果这个新的元素不存在于哈希表中,那么只需让j继续向后移动即可,代码如下:
/*solution1 hash-map+sliding window*/
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        if(s.empty())
            return 0;
        unordered_map<char,int> hash;
        int longest,res=0,i=0,j=0,size=s.size();
        string tmp;
        while(i<size&&j<size){
            if(hash.find(s[j])==hash.end()){
                hash[s[j++]]=j;
                res=max(res,j-i);
            }
            else    
                hash.erase(s[i++]);
        }
        return res;
    }
};

3.array instead of hash:

考虑到题目的特殊性,即样例中只有ASCII码,我们用一个长度为256的数组即可优化时间上的性能,由于我们可以对数组中的元素进行随机访问,所以不用像上面的解法一样进行删除元素的操作,只需要对其左边界调整到下一个位置即可,代码如下:

/*solution2*/
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        vector<int> mymap(256,-1);
        int i,last=0,res=0;
        for(i=0;i<s.length();i++) {
            if(mymap[s[i]]==-1 || mymap[s[i]]<last)
                res = max(res,i-last+1);
            else
                last = mymap[s[i]]+1;
            mymap[s[i]]=i;
        }
        return res;
    }
};

运行时间从86ms压缩到16ms,时间性能上有了一个巨大的提升。

知识共享许可协议
本作品采用知识共享署名 3.0 中国大陆许可协议进行许可。

我们在朋友圈里互相羡慕,然而生活的不容易只有自己才知道

有些事只有自己知道。

比如所有人都看到你去参加了奢华的酒会晚宴,纷纷点赞,却很少有人知道你一点都不喜欢在晚餐的时候假笑。

比如所有人都看到你夜夜笙歌,彻夜泡吧,觉得你活得自在,但很少有人想到如果你回家,在冰冷的床上辗转反侧根本睡不着。

在做垃圾桶,听大家心底话的那段时间里,我遇到过渴望有个家的骑行浪子,只渴望安静生活被尊重的同性权利KOL,离家出走一年的“独立”少年表示想再和父母睡一张床,而有稳定体面工作的女白领则大谈自己心中的文艺少女梦。

我也一样,在之前那个标题没起好的文章快来看看五月天的新歌MV吧:出道二十年,还在写梦想的结尾,我用自传的口吻写了一个故事,故事的最后是这样:

婚礼那天来了很多人,爸爸笑的开心,想必这就是他想象中我最好的生活。妈妈则在和亲戚朋友聊天,估计是在说她从小就觉得我能成为一名成功的作家。 同学们围坐了一桌,非要起哄要我上去给大家来一段演讲。 于是已经醉的不省人事的我大步走了上去。

“大家好,我叫雷斯林,今年28岁,长大了想成为一名科学家。”

虽然我们每个人都在朋友圈里活得多姿多彩,然而生活的艰辛只有自己才知道。

不止我们,从古至今那些被我们仰望的富人英雄,帝王将相也是一样。

曹操最早只想为国家讨贼立功,不想做成为天下人争议的枭雄,然而在历史的进程推动下,他早已退无可退。在《让县自明本志令》中,这位一辈子不敢登基的丞相解释道:

“孤始举孝廉,年少,自以本非岩穴知名之士,恐为海内人之所见凡愚,欲为一郡守,好作政教,以建立名誉,使世士明知之;故在济南,始除残去秽,平心选举,违迕诸常侍。以为强豪所忿,恐致家祸,故以病还。

去官之后,年纪尚少,顾视同岁中,年有五十,未名为老。内自图之,从此却去二十年,待天下清,乃与同岁中始举者等耳。故以四时归乡里,于谯东五十里筑精舍,欲秋夏读书,冬春射猎,求底下之地,欲以泥水自蔽,绝宾客往来之望。然不能得如意。”

以还执事,归就武平侯国,实不可也。何者?诚恐己离兵为人所祸也。

翻译成白话文大概说的是他本来只想做个郡守,结果被豪强迫害,被迫回家。回家后开了一家学社,在秋夏读书,冬春打猎,只希望得到一点瘠薄的土地,想老于荒野、不被人知,断绝和宾客交往的念头,又没能如意。

最后到了现在的位置上,如履薄冰,战战兢兢。精疲力尽了,想放弃军权返回封地,却很清楚地知道,如果自己放弃了军权,肯定会被人所害。

无论这中间有多少真话,都能看出一个在权力巅峰的花甲老人对无人打扰的清净生活的真情流露。

不知道当他看到诸葛孔明放弃隆中的生活,走上台前,会不会想到当年的自己而心生懊悔呢?

“吾好梦中杀人”很狡诈,但又有多少人看到了这种狡诈背后的如履薄冰

还有更早一点的李斯,文种也是一样。一辈子勤勤恳恳辅佐帝王,却在最后死于帝王之侧。

前者被秦二世胡亥处死,死前哭着和儿子回忆曾经在家门口牵着黄狗打猎的情形,但却永远都回不去了。

“吾欲与若复牵黄犬俱出上蔡东门逐狡兔,岂可得乎!”

后者被“卧薪尝胆”的勾践赐死,错过了范蠡劝他引退的唯一时机。不知道他自裁的时候会不会想到范蠡给他留下的十二字真言:

“蜚鸟尽,良弓藏;狡兔死,走狗烹”

然而无法后悔,有的路走上去了就下不来了。

文种

回到现代也是一样。

之前马云在俄罗斯圣彼得堡国际论坛上发言,表示创立阿里巴巴是他这生最大的错误,称因为阿里巴巴让他每天忙得像个总统一样,根本没有个人生活。

很多人觉得这是马云在装逼,但我却觉得这一定程度上是这位52岁男人的真心话。毕竟每天受万人瞩目,为千万人的饭碗甚至一个区域的经济发展负责,这对心智不坚定的人是灾难,即使对心智强大如马云,也可谓是巨大的压力。

就像我之前所说,一篇文章,写到千万阅读早就已经不是作者自己的了;同样的,一个企业做到百亿以上也早就不属于哪个个人或是哪个团体。很多时候某些大企业家也有自己的苦衷。

所以他坦言:

如果有来生,我希望有机会去到世界上任意一个国家,在那里平静度日。我不想谈论商业,不想工作。

每个看起来光鲜亮丽的人,背后可能都有说不清的心酸,都会羡慕别人的生活。只是他们中某些人的生活已经太重,已经很难掉头,所以只能一直沿着那条路走下去。但对我们中的大多数来说,生活才刚刚开始。如果对生活不满,与其羡慕他人,不如自己先试试看能否做出些改变。

毕竟体验过再说,比光是羡慕可有趣得多。

正是因为人生太重,我们其实并不会不计成本地真的从头到尾再活一次。现在闲鱼有一个有意思的活动,让你有机会可以体验不一样的“第二人生”。

文 / 菜刀少爷 来源:菜刀少爷(ID:MrCaiDao) 腾讯创业已获得授权

◆ ◆ ◆ ◆ ◆

两个月前,朋友的创业公司倒闭了。

朋友是温文尔雅的白面君子,有着光鲜的履历和出众的能力。和他聊天,永远觉得沐浴春风。温润如玉,充满魅力。朋友细致而体贴。

他记得你的生日时,并在那天给发送祝福和红包;

从大阪旅行回来,他会给你带的白色恋人作为礼品;

对于工作不达标的下属,他会耐心开导安慰;

他也会在众人之下为员工顾全面子,把到嘴边的批评指责咽进肚子。

他相信,在自由宽松的环境下,更有灵感更有效率。所以员工的上下班时间自己定:在完成任务的前提下,想来就来,想走就走。

每个人都很舒服,所有人都喜欢他。

朋友倾注了极大的心血,但在不温不火了一年多之后,项目终于挂掉了。安慰之余,我对他说,“你是个好人,但不是个好的管理者”。

慈不带兵,义不行贾。

◆ ◆ ◆ ◆ ◆

我的第一任领导,非常犀利。

他的思路敏捷而清晰,做事务实而高效,永远没有废话,永远雷厉风行。

和他共事,绝对不会轻松。他不会讲段子哄你干活,他也不会笑眯眯地安抚你的情绪。

每周的例会,是一场例行考试,每个人都小心翼翼、如履薄冰。如果顺利,他会扔个梗让大家笑一笑,然后收起脸继续挑战你;如果不顺利,他会打断你,生生把你拍在墙上,毫不留情,但有理有据。从此,你再也不敢造次。

他不会掩藏不爽,他对愚蠢的忍耐度很低,他说变脸就变脸。如果被他认定为“不求上进”,你就成了他眼中就是个废物,从此不会落下好脸色。

他有个口头禅,“什么意思”。如果他连问三遍,你的汗都会下来。

有时候,你简直觉得,他是刻薄冷酷的资本家,是不近人情的吸血鬼。

事情的另外一面是:下属犯下大错、魂飞魄散,他会一起加班,收拾残局;在家里,骂得你体无完肤,出去了,他替你出头;他会抓住机会,向大老板进言,不断为团队谋取福利;他会在下属家人过世时,额外批准两天假期……

再就是,团队高速运转,业绩喷薄增长。

说来有点残酷,但事实的真相是:评判一个管理者的好坏,从来不是看测验的民意,而是看输出的成绩。良好的干群关系和群众基础,有助于达到目的,但却不是最终目标。

一个好的经理人,就是要做出好数字,对老板负责;一个好的 CEO,就是要做高利润或股价,对股东负责。

这才是职业的素养,这才是商业的逻辑。

就像他常说的,“我是来把事情做成的,不是来交朋友的”。

◆ ◆ ◆ ◆ ◆

格雷格·波波维奇是马刺的主帅,是名副其实的人生赢家。

他是 NBA 最成功的教练之一,是北美职业体育史的传奇,也是美国国家队的主教练。执掌马刺二十年,三获年度最佳,一千场胜利,五个总冠军。

球场上的波波维奇,是出了名的暴君。

蛮横,独裁,暴躁,刻薄,六亲不认,追求细节,容不得沙子……

他是最难缠的教练,他不断的提出要求,忽略你的表现,如同你是世上最蠢的人。你犯了错?祝你好运了。那副愤怒咆哮的嘴脸,你几个星期都忘不掉。

他是个大混蛋,咆哮着,“打得更脏一些”;他是个混不吝,不会对任何人留下情面,不管明星球员,还是板凳替补。

▲ 独裁的暴君:波波维奇

球场下的波波维奇,众所周知,一个超级逗逼,一个大大的好人。

在他的治下,马刺成了一支欢乐奇葩的球队。逗逼故事数不胜数,球队氛围令人陶醉。这里全是有天赋的人,这里充满了对篮球的讨论,简直是篮球人的天堂。

▲ 逗逼马刺的 COSPLAY

他和弟子,彼此信任、感情深厚。有人说波波维奇是“所能遇到的内心深处最善良的人”。

在邓肯的退役发布会上,这样一位铁血教头声音颤抖、几度哽咽:“想和他告别不可能”。场面令人动容。

▲ 波波维奇和邓肯

他是世界上最坏的人,他压榨你的身体,他撕碎你的自尊,他是凶狠贪婪的吸血鬼。他又是世界上最好的人,他关心你的家庭,即使全世界抛弃了你,他还会陪着你,希望你能做到最好。

波波维奇是一个优秀的管理者样本。成功的教练都需要具备这两方面的性格,并在两者间切换自如。

场上暴君,场下朋友;工作魔鬼,生活好人。

◆ ◆ ◆ ◆ ◆

白崇禧教育儿子的故事挺好玩。

儿子白先敬喜欢打架,白崇禧先问,”你欺负别人,还是别人欺负你?”如果是欺负别人,儿子当场要挨顿打,然后拎着耳朵到人家里道歉。如果是被人家欺负了,他会问,“你回手了没有?”如果没有,这顿打挨得比前面更惨。

懂得战争,但不发起战争。

李嘉诚教育子孙:做人的最高境界是“仁慈的狮子”。仁慈是本性,但单靠仁慈,还无法成功。要有狮子的力量,才能赚钱养家,才能保护亲人,才能反抗欺压。

“做个好人”和“做个好的管理者”,其实并不冲突。

据说,柳传志的情商超高,接触过他的人都被其人格魅力折服。你以为他只是个慈祥的老头?看看他处理孙宏斌、倪光南的恩恩怨怨,那真是雷霆万钧、毫不犹豫。

世上最讨厌的词是“烂好人”。善良发展到极致,就是无原则的妥协。发生在欧洲大陆的暴恐事件,就是现代版的《农夫与蛇》。

要做好人,但不要做烂好人。

很多时候,做烂好人不是因为品德高尚,而是因为技术不够。你不懂地拒绝,不懂压力传递,不懂当断则断,不懂“扔掉背上的猴子”,不懂“挥泪斩马谡”,不懂优雅地撕逼……

狡猾如狐狸,善良如鸽子。

◆ ◆ ◆ ◆ ◆

对于我的第一任领导,我曾经也不理解:觉得他不近人情、太过苛刻。当我到了新的岗位,当我成为中坚力量,我开始感谢那段时间的高压锻造和扎实积累。

带团队不是请客吃饭。嘻嘻哈哈、舒服安逸不会有执行力;温良恭俭、皆大欢喜不会有战斗力。

不要放纵纪律,不要对制度宽容,这是对员工的不负责;要帮助员工成长,提升收入,获得更体面的生活,这是对员工最大的负责。

胡林翼曾送给晚年的曾国藩一副寿联:“以雷霆手段,显菩萨心肠”。曾国藩阅后百感交集、热泪盈眶。

委屈误解是人生的必修课,双重人格是领袖的铁布衫。

送给每一位管理者。

49. Group Anagrams

Given an array of strings, group anagrams together.

For example, given: ```[“eat”, “tea”, “tan”, “ate”, “nat”, “bat”]``, Return:

[
  ["ate", "eat","tea"],
  ["nat","tan"],
  ["bat"]
]

Note: All inputs will be in lower-case.

这道题需要我们对给出的一组混杂在一起的变位词进行分类,分类的依据是:如果一个词进行变位之后,其对应的变位词存在于给定的向量中,则将这两个词加入新向量的一个分量里,如果还有,则追加入该分量中。

看起来是一道很麻烦的题,最初我考虑以某一个词为标准,列出其组成字母的全排列,然后再与后面的词进行对比。这个方法的难点就在于如何写出全排列,于是这个思路受阻,开始从别的方向考虑。

我们发现互为变位词的若干个词有一个相似之处,就是在经过排序之后完全相同,那么这样我们可以利用这个性质,让排序后的字符串作为主键,让这几个词组成的集合成为主键映射的值。

之后我们遍历哈希表,将主键相同的成员放入同一个分量即可,代码如下:

因为题目中说明只需要考虑小写字母即可,所以为加快排序速度我采用计数排序而不是库函数中的排序算法

/*version 1.0*/
class Solution {
private:
    string strSort(string& str){
        int count[26]={0},p=0,size=str.size();
        string res(size,'a');
        for(int i=0;i<str.size();i++)
            count[str[i]-'a']++;
        for(int i=0;i<26;i++)
            for(int j=0;j<count[i];j++)
                res[p++]+=i;   
        return res;
    }
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        if(strs.empty())
            return {};
        unordered_map<string,vector<string>> hash;
        vector<vector<string>> anagrams;
        for(string s:strs){
            string t=strSort(s);
            // sort(t.begin(),t.end());
            hash[t].push_back(s);
        }
        for(unordered_map<string,vector<string>>::iterator pos=hash.begin();pos!=hash.end();pos++){
            vector<string> anagram(pos->second.begin(),pos->second.end());
            anagrams.push_back(anagram);
        }
        return anagrams;
    }
};

但这里返回的结果和答案在顺序上有一定的差别,仔细看了一下发现答案要求要按首字母从前向后的关系来排列,所以为了维护这样的顺序,我们采用multiset来存储主键相同的元素,而不再使用vector,因为multiset本身内部会使元素有序,所以我们只需利用这一性质即可。

代码如下:

/*version 2.0*/
class Solution {
private:
    string strSort(string& str){
        int count[26]={0},p=0,size=str.size();
        string res(size,'a');
        for(int i=0;i<str.size();i++)
            count[str[i]-'a']++;
        for(int i=0;i<26;i++)
            for(int j=0;j<count[i];j++)
                res[p++]+=i;   
        return res;
    }
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        if(strs.empty())
            return {};
        unordered_map<string,multiset<string>> hash;
        vector<vector<string>> anagrams;
        for(string s:strs){
            string t=strSort(s);
            // sort(t.begin(),t.end());
            hash[t].insert(s);
        }
        for(unordered_map<string,multiset<string>>::iterator pos=hash.begin();pos!=hash.end();pos++){
            vector<string> anagram(pos->second.begin(),pos->second.end());
            anagrams.push_back(anagram);
        }
        return anagrams;
    }
};

非常有意思的一道题,值得我反复琢磨。

知识共享许可协议
本作品采用知识共享署名 3.0 中国大陆许可协议进行许可。

274. H-Index

Given an array of citations (each citation is a non-negative integer) of a researcher, write a function to compute the researcher’s h-index.

According to the definition of h-index on Wikipedia: “A scientist has index h if h of his/her N papers have at least h citations each, and the other N − h papers have no more than h citations each.”

For example, given citations = [3, 0, 6, 1, 5], which means the researcher has 5 papers in total and each of them had received 3, 0, 6, 1, 5 citations respectively. Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, his h-index is 3.

Note: If there are several possible values for h, the maximum one is taken as the h-index.

Hint:

An easy approach is to sort the array first.

What are the possible values of h-index?

A faster approach is to use extra space.

这道题让我们计算一个科研人员的H指数,所谓H指数就是一个人在其所有学术文章中有N篇论文分别被引用了至少N次,他的H指数就是N。

定义比较简单,维基百科提供了一种很简单的算法:

  1. 将其发表的所有SCI论文按被引次数从高到低排序;
  2. 从前往后查找排序后的列表,直到某篇论文的序号大于该论文被引次数。所得序号减一即为H指数。

根据上面的算法可以很容易写出正确的程序,代码如下:

/*solution1 as the definition ,4ms*/
class Solution {
public:
    int hIndex(vector<int>& citations) {
        if(citations.empty())
            return 0;
        sort(citations.begin(),citations.end(),greater<int>());
        for(int i=0;i<citations.size();i++){
            if(i>=citations[i])
                return i;
        }
        return citations.size();
    }
};

在查看了其他人的讨论后,我又获得了一种新的思路,即数组映射法,可以去掉排序的过程。

首先对数组进行遍历,用一个新数组记录引用次数相同的文章数目,这里需要注意的是,如果某篇文章引用次数超过了数组长度n,则将这个引用次数看成n即可,因为H指数不可能超过数组长度,之后,我们从次数i等于n的文章开始,统计次数大于等于i的文章数目之和,因为大于等于i的文章数目等于次数大于等于i+1的文章数目加上次数等于i的文章数目。

如果计算出文章数目之和大于等于引用次数i的值,则说明该引用次数的值即为H指数。

代码如下:

/*solution2 */
class Solution {
public:
    int hIndex(vector<int>& citations) {
        if(citations.empty())
            return 0;
        int size=citations.size(),sum=0;
        vector<int> count(size+1,0);
        /*统计引用次数为i的文章数目*/
        for(int i=0;i<size;i++)
            count[citations[i]>size?size:citations[i]]++;
        for(int i=size;i>=0;i--){
            /*从最后一个开始,引用次数大于等于i的文章数目是引用次数大于等于i+1的文章数加上引用次数等于i的文章数目*/
			sum+=count[i];
            if(sum>=i)
                return i;
        }
        return -1;
    }
};

知识共享许可协议
本作品采用知识共享署名 3.0 中国大陆许可协议进行许可。