原题网址:https://www.lintcode.com/zh-cn/old/problem/minimum-window-substring/#
32. 最小子串覆盖
讨论区
给定一个字符串source和一个目标字符串target,在字符串source中找到包括所有目标字符串字母的子串。
注意事项
如果在source中没有这样的子串,返回"",如果有多个这样的子串,返回起始位置最小的子串。
说明
在答案的子串中的字母在目标字符串中是否需要具有相同的顺序?
——不需要。
样例
给出source = "ADOBECODEBANC",target = "ABC" 满足要求的解 "BANC"
标签
思路:
使用哈希表,建立字符(索引)- 字符数量(值)的映射。
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);
}
}
};