主要想法为:
建立一个hashmap<String ,int>保存所有words中的单词,记录下它们各自出现的次数。然后外层循环从文本串中依次取下长度等于words中单词拼接后长度的子串,程序中为变量ps。内层循环从ps中每次截取一个单词,这时我们拷贝一份先前的hashmap,这个单词出现一次,就在hashmap的指定键值处减一,如果发生有一个值被减到小于0,则是一次失败的匹配,内层循环退出。同时,如果根本没有找到这个从ps里截到的单词,同样查找失败,内层循环退出。
当然到这里还是不够的,如果文本串中大量出现了words中的单词,这个方法的效率就会很低。这里做了优化:建立一个hashset保存着已扫描的所有失败案例,当新的ps在这个失败案例集中时,直接判定失败。
public List<Integer> findSubstring(String s, String[] words) {
int N = s.length();
int M = words.length;
HashMap ct = new HashMap();
HashSet<String> hs = new HashSet();
List<Integer> result = new LinkedList();
for(int i=0;i<M;i++){
if(ct.containsKey(words[i])){
int c = (int) ct.get(words[i]);
ct.put(words[i], ++c);
}else{
ct.put(words[i], 1);
}
}
int wlen = words[0].length();
int getj = 0;
for(int i=0;i<N-wlen*M+1;i++){
String ps = s.substring(i,i+wlen*M);
if(hs.contains(ps)){
continue;
}
HashMap nc = (HashMap) ct.clone();
getj=0;
for(int j=0;j<wlen*M-wlen+1;j=j+wlen,getj=j){
String tryfind = ps.substring(j,j+wlen);
if(nc.containsKey(tryfind)==false){
hs.add(ps);
break;
}else if((int) nc.get(tryfind)>0){
int c = (int) nc.get(tryfind);
c--;
nc.put(tryfind, c);
}else{
hs.add(ps);
break;
}
}
if(getj>=wlen*M-wlen+1)
result.add(i);
}
return result;
}