题目
分析
两种解法。
都使用到了hash的知识。对于第一种解法,我们比较直接的循环得到所有可能的结果,也就是和单词数组长度和相同的所有子串,从i=0开始,一直到最后,对于每一个循环得到的子串就进行判断即可,判断就是通过比较两个哈希是否相同。
for(int i=0;i<S.length()-all_len+1;i++) { String temp_str=S.substring(i,i+all_len);//每一次循环得到的和单词数组长度相同的子串 //System.out.println(temp_str); HashMap<String,Integer> temp_map=new HashMap<>(); for(int j=0;j<temp_str.length()-one_word+1;j+=one_word)//然后对这个字符串一个单词一个单词的进行拆分 { String temp_temp_str=temp_str.substring(j,j+one_word); temp_map.put(temp_temp_str,temp_map.getOrDefault(temp_temp_str,0)+1); } //然后进行比较,相同的话,就是符合条件的 if(map.equals(temp_map)) { System.out.println(i); res.add(i); } }
然后就是解法2了,对于解法2我们使用到了滑动窗口的概念,当然也是通过比较hash来解题的,看一下核心的代码
for(int i=0;i<oneWord;i++) { int left=i; int right=i; int count=0; HashMap<String,Integer> tempMap=new HashMap<>(); while(right+oneWord<=wordLength) { String tempStr=S.substring(right,right+oneWord); tempMap.put(tempStr,tempMap.getOrDefault(tempStr,0)+1); count++; right+=oneWord; //一个判断条件实则是对两种情况的判断 //case1 如果加入的tempStr是字符数组中没有出现过的话, // 我们就要一直从左边出队,直到这个不符合条件的出了队,也就是将整个队列中的字符全部出队 //case2 如果是数量过多的话,我们能做的也只是从左边一直出队 //两种情况一中策略 while(tempMap.getOrDefault(tempStr,0)>map.getOrDefault(tempStr,0)) { String temptempStr=S.substring(left,left+oneWord); tempMap.put(temptempStr,tempMap.getOrDefault(temptempStr,0)-1); count--; left-=oneWord; } if(count==wordLength) res.add(left); } }
第一个问题,最外层的for循环
for(int i=0;i<oneWord;i++)
我们分了三个路径,分别是如下
从i=0开始,每次走单个单词的长度 从i=1开始,每次走单个单词的长度 从i=2开始,每次走单个单词的长度
这样和从i=0开始,每次走一个覆盖的是相同的。
然后就是滑动窗口的进和出
//最开始left和right都是起始位置 while(right+oneWord<=wordLength) { //每次循环都将一个单词长度的加入到队列中 String tempStr=S.substring(right,right+oneWord); tempMap.put(tempStr,tempMap.getOrDefault(tempStr,0)+1); count++; right+=oneWord;
上面的代码就是进入窗口的代码
//一个判断条件实则是对两种情况的判断 //case1 如果加入的tempStr是字符数组中没有出现过的话, // 我们就要一直从左边出队,直到这个不符合条件的出了队,也就是将整个队列中的字符全部出队 //case2 如果是数量过多的话,我们能做的也只是从左边一直出队 //两种情况一中策略 while(tempMap.getOrDefault(tempStr,0)>map.getOrDefault(tempStr,0)) {
对于循环条件,注解已经给出了很详细的解释了,但是对于没有进入的情况,表面现在的temp哈希是符合条件的,只需要再满足count的数量就是一个解了
String temptempStr=S.substring(left,left+oneWord); tempMap.put(temptempStr,tempMap.getOrDefault(temptempStr,0)-1); count--; left-=oneWord; } if(count==wordLength) res.add(left); }
代码展示
解法1:
public static ArrayList<Integer> findSubstring(String S, String[] L) { ArrayList<Integer> res=new ArrayList<>(); int one_word=L[0].length(); int word_length=L.length; int all_len=one_word*word_length; HashMap<String,Integer> map=new HashMap<>(); //init for(int i=0;i<L.length;i++) { map.put(L[i],map.getOrDefault(L[i],0)+1); } //do for(int i=0;i<S.length()-all_len+1;i++) { String temp_str=S.substring(i,i+all_len); //System.out.println(temp_str); HashMap<String,Integer> temp_map=new HashMap<>(); for(int j=0;j<temp_str.length()-one_word+1;j+=one_word) { String temp_temp_str=temp_str.substring(j,j+one_word); temp_map.put(temp_temp_str,temp_map.getOrDefault(temp_temp_str,0)+1); } if(map.equals(temp_map)) { System.out.println(i); res.add(i); } } return res; }
解法2:
public ArrayList<Integer> findSubstring(String S, String[] L){ ArrayList<Integer> res=new ArrayList<>(); HashMap<String,Integer> map=new HashMap<>(); int oneWord=L[0].length(); int wordLength=L.length; int allLen=oneWord*wordLength; //init for(int i=0;i<L.length;i++) { map.put(L[i],map.getOrDefault(L[i],0)+1); } //三种开始的位置 for(int i=0;i<oneWord;i++) { int left=i; int right=i; int count=0; HashMap<String,Integer> tempMap=new HashMap<>(); while(right+oneWord<=S.length()) { String tempStr=S.substring(right,right+oneWord); tempMap.put(tempStr,tempMap.getOrDefault(tempStr,0)+1); count++; right+=oneWord; //一个判断条件实则是对两种情况的判断 //case1 如果加入的tempStr是字符数组中没有出现过的话, // 我们就要一直从左边出队,直到这个不符合条件的出了队,也就是将整个队列中的字符全部出队 //case2 如果是数量过多的话,我们能做的也只是从左边一直出队 //两种情况一中策略 while(tempMap.getOrDefault(tempStr,0)>map.getOrDefault(tempStr,0)) { String temptempStr=S.substring(left,left+oneWord); tempMap.put(temptempStr,tempMap.getOrDefault(temptempStr,0)-1); count--; left+=oneWord; } if(count==wordLength) res.add(left); } } Collections.sort(res); return res; }
学习情况
实现两种解法,一次,很重要的一道题。