原题网址:https://www.lintcode.com/zh-cn/old/problem/minimum-window-substring/#
32. 最小子串覆盖
给定一个字符串source和一个目标字符串target,在字符串source中找到包括所有目标字符串字母的子串。
注意事项
如果在source中没有这样的子串,返回"",如果有多个这样的子串,返回起始位置最小的子串。
您在真实的面试中是否遇到过这个题?
Yes
说明
在答案的子串中的字母在目标字符串中是否需要具有相同的顺序?
——不需要。
样例
给出source = "ADOBECODEBANC",target = "ABC" 满足要求的解 "BANC"
挑战
要求时间复杂度为O(n)
标签
思路:
使用哈希表,建立字符(索引)- 字符数量(值)的映射。
ASCII码表常用字符128个,建立两个容量为128的数组tcount[128]与scount[128],分别表示target中相应字符数量、source上的移动窗(子串)中相应字符数量,初始时两数组相应元素都为0;
遍历target,计算每个字符出现的次数,次数值存放在tcount【128】中;
定义int型变量begin = -1、end = source.size()、start = 0,found=0, 以及minLen = source.size(),分别代表移动窗起始位置、结束位置,窗口的移动索引,已经找到的目标串字符数量,窗口的最小长度(end-begin);
遍历source,如果source【i】对应字符的数量加1后小于或等于该字符在target中的数量,说明找到一个字符,则found++;
若found等于target.size(),说明找到一个子串,这时就要确定移动窗的大小,即去掉前面那些无效的字符(target中没有但子串中有的字符),相当于确定移动窗起始位置。
方法是移动start,如果scount[source[start]] > tcount[source[start]] ,说明是无效字符(两种情况:窗内存在该字符但目标中不存在,舍弃。另一种窗内该字符数量超过目标中数量,舍弃;),则scount[source[start]]--; start++;
重复上述过程直到scount[source[start]] = tcount[source[start]],即找到了移动窗的起始位置。
这时再判断当前窗长度是否小于minLen,小于则更新移动窗,即 begin = start;end = i;minLen = i-start;
然后移动窗在源串上前移一位,注意舍弃起始位置字符(scount[source[start]]--; found--; start++;),继续寻找下一个子串;
循环结束后若begin==-1说明没找到子串,否则返回source.substr(begin,minLen+1);
AC代码:
class Solution { public: /** * @param source : A string * @param target: A string * @return: A string denote the minimum window, return "" if there is no such a string */ string minWindow(string &source , string &target) { // write your code here if (source.empty()) { return ""; } int ssize=source.size(); int tsize=target.size(); int tcount[128]; int scount[128];//移动窗内相应字符的数量; for (int i=0;i<128;i++) { scount[i]=0; tcount[i]=0; } for (int j=0;j<tsize;j++) { tcount[target[j]]++; } int begin=-1,end=ssize,found=0,minLen=ssize;//初始化移动窗起始、终止位置、寻找过程中的子串累积长度,最小移动窗长度; for (int i=0,start=0;i<ssize;i++) { scount[source[i]]++;//遍历源串,如果这个字符的数量加1后不超过目标串中该字符的数量,则找到了一个匹配字符,found+1; if (scount[source[i]]<=tcount[source[i]]) { found++; } if (found==tsize)//找到一个子串; { while(start<i&&scount[source[start]]>tcount[source[start]])//去掉无效字符,缩短移动窗长度; { //两种情况:窗内存在该字符但目标中不存在,舍弃。另一种窗内该字符数量超过目标中数量,舍弃; scount[source[start]]--;//移动窗内去掉这个字符; start++;//继续向前判断; } //最后的结果是start对应的字符是目标中的某个字符; if (i-start<minLen) { minLen=i-start; begin=start; end=i; } //移动窗在源串上前移一位(舍弃起始字符),继续寻找子串; scount[source[start]]--; found--; start++; } } if (begin==-1) { return ""; } else { return source.substr(begin,minLen+1); } } };