hashtable+双指针窗口法解题思路
最近一直在刷leetcode,发现许多题目解法很相似,如求 findSubstring,minWindow,lengthOfLongestSubstring问题,解法大致都一样都是用滑动窗口的思想,不过滑动的规则没处理好,很容易导致时间复杂度增加;
题目链接:https://leetcode.com/problems/substring-with-concatenation-of-all-words/;
http://oj.leetcode.com/problems/minimum-window-substring/ ;
http://oj.leetcode.com/problems/longest-substring-without-repeating-characters/ ;
//链接1题解
public ArrayList<Integer> findSubstring(String S, String[] L) {
// Note: The Solution object is instantiated only once and is reused by each test case.
ArrayList<Integer> res = new ArrayList<Integer>();
if(S==null || S.length()==0 || L==null || L.length==0)
return res;
HashMap<String,Integer> map = new HashMap<String,Integer>();
for(int i=0;i<L.length;i++)
{
if(map.containsKey(L[i]))
{
map.put(L[i],map.get(L[i])+1);
}
else
{
map.put(L[i],1);
}
}
for(int i=0;i<L[0].length();i++)
{
HashMap<String,Integer> curMap = new HashMap<String,Integer>();
int count = 0;
int left = i;
for(int j=i;j<=S.length()-L[0].length();j+=L[0].length())
{
String str = S.substring(j,j+L[0].length());
if(map.containsKey(str))
{
if(curMap.containsKey(str))
curMap.put(str,curMap.get(str)+1);
else
curMap.put(str,1);
if(curMap.get(str)<=map.get(str))
count++;
else
{
while(curMap.get(str)>map.get(str))
{
String temp = S.substring(left,left+L[0].length());
if(curMap.containsKey(temp))
{
curMap.put(temp,curMap.get(temp)-1);
if(curMap.get(temp)<map.get(temp))
count--;
}
left += L[0].length();
}
}
if(count == L.length)
{
res.add(left);
//if(left<)
String temp = S.substring(left,left+L[0].length());
if(curMap.containsKey(temp))
curMap.put(temp,curMap.get(temp)-1);
count--;
left += L[0].length();
}
}
else
{
curMap.clear();
count = 0;
left = j+L[0].length();
}
}
}
return res;
}
/*复杂付分析:外循环L[0].length()次,内循环n/L[0].length()次,每次访问的字符串的总次数不超过2n此,每个word最多会被窗口两端各访问一次,总的时间复杂度O(n)*/
public int lengthOfLongestSubstring(String s) {
//双指针+hashtable(int数组实现)
if(s==null||s.equals("")) return 0;
int max=1;
int[] wind=new int[256];
int start=0,end=0;
Arrays.fill(wind, -1);
for(;end<s.length();end++){
if(wind[s.charAt(end)]>=start)
start=wind[s.charAt(end)]+1;
wind[s.charAt(end)]=end;
max=Math.max(max,end-start+1);
}
return max;
}
public String minWindow(String S, String T) {
if(S==null || S.length()==0)
return "";
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
for(int i=0; i<T.length();i++)
{
if(map.containsKey(T.charAt(i)))
{
map.put(T.charAt(i),map.get(T.charAt(i))+1);
}
else
{
map.put(T.charAt(i),1);
}
}
int left = 0;
int count = 0;
int minLen = S.length()+1;
int minStart = 0;
for(int right=0; right<S.length();right++)
{
if(map.containsKey(S.charAt(right)))
{
map.put(S.charAt(right),map.get(S.charAt(right))-1);
if(map.get(S.charAt(right))>=0)
{
count++;
}
while(count == T.length())
{
if(right-left+1<minLen)
{
minLen = right-left+1;
minStart = left;
}
if(map.containsKey(S.charAt(left)))
{
map.put(S.charAt(left), map.get(S.charAt(left))+1);
if(map.get(S.charAt(left))>0)
{
count--;
}
}
left++;
}
}
}
if(minLen>S.length())
{
return "";
}
return S.substring(minStart,minStart+minLen);
}
关键点分析:
1,窗口左右端什么时候移,每次移动多少距离;
2,窗口内包含的字符序列是否符合要求;一般做法:将题目要求序列放入hashmap或字符数组中,然后遍历字符串的过程中碰到指定字符进行计数count,同时对hashmap进行增减处理,当count符合要求序列长度时停止;然后移动窗口,开始新的循环;左端移动时,对hashmap对应value进行加一处理,右端移动对hashmap对应value进行减一处理;
参考:
http://blog.csdn.net/linhuanmars/article/details/20343903;