好题,字符串,线性时间。
我觉得第一次拿到这个题的人应该不会知道该怎么做吧,要么就是我太弱了。。先搞清楚这个题要求的是什么。从一个长字符串中找一个字串,这个字串中的字符完全包含了另一个给定目标串中的字符,且这个字串的长度要求最小。还有一个非常重要的简化,题干指明了要求这种最短字串只有一个,这个限制其实暗示了这道题的整体思路,只要找到一个长串,然后缩减到不能缩减即可。
从题目的要求出发可以发现,这道题对于字符串中字符的顺序是没有要求的,因此可以很自然的想到用hash表来保存目标串中的每个字符的个数,然后在源字符串中找到一个字串,包含的字符个数大于等于目标串中的即可。难就难在怎么实现这个功能。
可以把目标串中每个字符的个数作为它的需求,每当在长字符串中找到了一个字符,这个字符在目标串中存在,那么需求应该减少1,当需求等于0的时候,表示到目前为止,长字符串中的字符可以恰好满足目标串中这个字符的需求了,如果一个目标串字符的需求变成了负值,说明在当前长度下,在长字符串中对这个字符的供应过剩了。为了描述简便,我定义当长字符串在目标串对这个字符的需求为正时提供了这个字符,为满足了刚性需求,否者是供应过剩。接下来还有一个问题,怎样知道目标串被完全满足了呢?你当然可以去逐个的扫描需求是不是都变成非正的了,但是还有一个更加简单的方法,那就是把目标串的长度看做是总需求,当满足刚性需求时,总需求减1,当总需求变成0时,说明目标串被满足了。
上面描述的过程在长字符串中找到了一个字串,可以完全满足目标串,但并不保证是最短的,因为很多字符在其他字符没得到满足时已经供应过剩了,怎样把这部分多余的去掉呢?从起点开始往后扫秒长字符串,如果当前字符根本不存在于目标串中,可以直接pass,如果存在于目标串中,且供应过剩了,那么这个字符可以从我们的字串中去掉,相当于我们的字串缩短了,但是要记得把需求量++,因为供应量减少了。知道一个字符,它既存在于目标串中,且他的需求量正好是等于0的,我们就得停下了,因为这时候的全部是刚性需求,不能再减少供应了。
代码如下,并没有最优化,不过思路是写出来了。
class Solution { public: string minWindow(string S, string T) { int ct1[270], ct2[270]; int mlen1 = S.length(); int mlen2 = T.length(); memset(ct1, 0, sizeof(ct1)); memset(ct2, 0, sizeof(ct2)); int hole = mlen2, minSize = INT_MAX, start = 0, minstart; for(int i=0;i<mlen2;i++){ ct1[T[i]]++; ct2[T[i]]++; } for(int i=0;i<mlen1;i++){ if(ct2[S[i]]>0){ ct1[S[i]]--; if(ct1[S[i]]>=0) hole--; } if(hole == 0){ while(start<mlen1){ if(ct2[S[start]]>0){ if(ct1[S[start]]<0) ct1[S[start]]++; else break; } start++; } if(minSize>i-start+1){ minSize = i-start+1; minstart = start; } } } if(minSize == INT_MAX) return ""; return S.substr(minstart, minSize); } };