1.问题描述:
给定一个字符串S和非空字符串P,在S中找出所有P的变位词的起始索引,例如:
输入:
s: "cbaebabacd" p: "abc"
输出:
[0, 6]
2.问题分析:
从问题的描述可以看出所谓的变位词只是顺序可以改变,其他的一切不能改变,我首先想到的是从S中逐一取出字符然后在P进行匹配,匹配长度为P.length(),但是这样存在一个为题——S中连续重复出现P中的字母例如:输入:s: "cbaababaca" p: "abc" ,输出:[0,1,2,3,4,5,6,7],显然这些起始并非都是P的变位词,出现这种问题的原因在于重复使用了P中的某个字符,对症下药——对P中的字符进行标记,从而避免对P中字符重复匹配而产生的上述问题。我们可以用map对P中的每个字母进行标记,这里我们使用数组作为标记,同时介绍一种“滑动窗口”的思想。
3.窗临汴河水,门渡楚门人
public List<Integer> findAnagrams(String s, String p) {
List<Integer> results = new ArrayList<Integer>();
if(s==null || s.length() < p.length()) return results;
char[] cp = p.toCharArray();
int[] hash = new int[256];
for(char c : cp) hash[c]++;
int left = 0, right = 0, counter = 0;
while(right < s.length()){
if(hash[s.charAt(right++)]-- >= 1)counter++;
if(counter==p.length()) results.add(left);
if(right-left == p.length() && hash[s.charAt(left++)]++ >= 0)counter--;
}
return results;
}
注释:用hash数组对P中的字符进行标记,核心逻辑于while中的三个if中:1.根据hash数组的标记进行计数;2.匹配完成,记录结果;3.撑开窗口,进行移动。4.由此及彼,见异思迁
给定一个字符串S和字符串T,在S中找出包含T的最短字符串,例如:S = "ADOBECODEBANC" T = "ABC" result = "BANC"。
public String minWindow(String s, String t) {
String results = new String();
if(s==null || t==null || t.length() == 0 || s.length() < t.length()) return results;
char[] ct = t.toCharArray();
int[] hash = new int[256];
for(char c : ct){
hash[c]++;
}
//open window
int left = 0, right = 0, begin = 0, range = s.length()+1, counter=0;
while(right < s.length()){
if(hash[s.charAt(right++)]-- >=1)counter++ ;
while(counter==t.length()){
if(range > right-left){
range = right - (begin=left);
results = s.substring(begin,begin+range);
}
if(hash[s.charAt(left++)]++ >=0 )counter--;
}
}
return results;
}
注释:核心代码同样是位于while中,不同的是其中嵌套了一个while,内部这个while的目的是缩小窗口+移动窗口,巧妙之处在 if 判断之处对窗口进行了缩小,用 if 主体代码跳出循环移动窗口!
5.窗口体会:
left和right撑开窗口,counter记录窗口风景,left自增——窗口(缩小)移动扫描目标字符串,同时hash数组记录预定字符串中每一个字符的状态是解决问题的另一个关键点!