您的位置:首页 > 其它

LeetCode 30 Substring with Concatenation of All Words 解法为Hashmap

2015-08-25 23:01 483 查看
Substring with Concatenation of All Words

You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation
of each word in wordsexactly once and without any intervening characters.

For example, given:

s:
"barfoothefoobarman"


words:
["foo", "bar"]


You should return the indices:
[0,9]
.

(order does not matter).
解题思路:首先,理解题意很重要。由于我之前没有理解题意,靠测试用例理解,走了不少弯路。题意是返回字符串中的字符串片段,这样的组合涵盖words数组的每个字符串。若words中包含重复项,字符串组合中也应当有重复项。比如:wordgoodgoodgoodbestword,{"word","good","best","good"} 返回 8,就是从第8个起的子串涵盖words数组。
在这里,得以复习hashmap,并利用数组条件,减少使用map接口的频率,从而降低时间消耗。

由于存在重复项的问题,为此,我们设立两个map,一个记录words的字符串出现次数,一个作为临时map,记录每次截取的字符串个数。若出现不是第一个map的子串则跳过,若扫描个数出现大于第一个map的个数则跳过。若完成遍历的时候,计数与words长度相同,不需要map的相等判断,直接可以判断符合条件。因为,不符合的情况,已经全部被过滤。

public List<Integer> findSubstring(String s, String[] words) {
int len = s.length();
int wlen = words.length;
int slen =words[0].length();
int end = len-wlen*slen;
//判断是否都满足子串数组
HashMap<String, Integer> map = new HashMap<String, Integer>();
HashMap<String, Integer> map1 = new HashMap<String, Integer>();//临时map
List<Integer> ls =new ArrayList<Integer>();
for (int i = 0; i < wlen; i++) {
if(map.containsKey(words[i])){
map.put(words[i], map.get(words[i])+1);
}else{
map.put(words[i], 1);
}
}
int i=0;
while(i<=end){
String str = s.substring(i, i+slen);
if(map.containsKey(str)){
//判断满足出现的组合contain
if(map1.containsKey(str)){
map1.put(str, map1.get(str)+1);
}else{
map1.put(str, 1);
}
int t =i+slen;
int j;
for ( j = 1; j < wlen; j++) {
if(t+slen>len){
return ls;
}
String str1 = s.substring(t, t+slen);
if(map.containsKey(str1)){
if(map1.containsKey(str1)){
map1.put(str1, map1.get(str1)+1);
}else{
map1.put(str1, 1);
}
}else{
break;
}
t+=slen;
if(map1.get(str1)>map.get(str1)){//这个
break;
}
}
if(j==wlen){//这个
ls.add(i);
}
}
i++;
map1.clear();
}
return ls;
}


上述对算法的优化参考自http://www.cnblogs.com/zhaolizhen/p/SubCon.html

这里,再给出一个discuss上的O(n)的解法:

public List<Integer> findSubstring(String S, String[] L) {
List<Integer> res = new ArrayList<>();
if (L.length == 0) {
return res;
}
int len = L[0].length();
int num = L.length;
if (len * num > S.length()) {
return res;
}

//histogram of words in L
HashMap<String, Integer> dic = new HashMap<>();
for (String s : L) {
if (dic.containsKey(s)) {
dic.put(s, dic.get(s) + 1);
} else {
dic.put(s, 1);
}
}

//the word that starts from i in S
String[] sDic = new String[S.length() - len + 1];
for (int i = 0; i < sDic.length; i++) {
String sub = S.substring(i, i + len);
if (dic.containsKey(sub)) {
sDic[i] = sub;
} else {
sDic[i] = "";
}
}

//traverse in order of 0,0+len,...,1,1+len,...len-1,len-1+len...therefore it is O(n) despite of two loops
for (int i = 0; i < len; i++) {

//start of concatenation
int start = i;
//number of words found
int found = 0;
//dynamic word histogram of words in substring(start,j);
HashMap<String, Integer> tempDic = new HashMap<>();
for (int j = i; j <= S.length() - len; j = j + len) {
String word = sDic[j];
if (word.equals("")) {
tempDic = new HashMap<>();
start = j + len;
found = 0;
continue;
} else {
if (!tempDic.containsKey(word)) {
tempDic.put(word, 1);
} else {
tempDic.put(word, tempDic.get(word) + 1);
}
found++;
}
//if we over-count a word, delete the first word in front. Also delete the words before that.
if (tempDic.get(word) > dic.get(word)) {
while (!sDic[start].equals(word)) {
tempDic.put(sDic[start], tempDic.get(sDic[start]) - 1);
start += len;
found--;
}
tempDic.put(word, tempDic.get(word) - 1);
start += len;
found--;
}
if (found == num) {
res.add(start);
}

}

}
return res;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: