You are given a string, S, and a list of words, L, that are all of the same length. Find all starting indices of substring(s) in S that is a concatenation of each word in L exactly once and without any intervening characters.
For example, given:
S:
"barfoothefoobarman"L:
["foo", "bar"]
You should return the indices:
[0,9].(order does not matter).
A:
哦,这道题,一开始是理解错了题目意思了。
仔细研读要求。是对每一个substring,例如从0 开始的,还有从9开始的substring(长度为L的总长度), 里面包含了L里的所有的non-intervening 组合。就是把所有的L的string连起来。
例如,上面例子中从0 开始的: barfoo 就是L的一个组合。 同样,从9开始的,foobar 也是一个组合。
假设L 里一共有n个string,每个string 长度为 m
分析:
1:我们不能穷尽L的组合形式。 2^n
2: 我们如果从每一个节点都开始计算L, 有点儿太啰嗦的感觉。 想想有没有什么好方法,来跳过部分预先监测的结果。
3: NOTE: strings in L, might exist replication.
4: 初步设想,用一个 list 记录所有match 的节点。然后看后面的substring是否match。 这样,问题就归结于该list里面的值,是否能凑够0到n-1个match。--------但这里有个问题,就是L中 可能有重复的情况。需要注意对待。
(问题就归结到,一个list,每个点包含了match的位置,如果全部的,都有0到n-1都能match的话,就加入result中,否则 移动到下一个点。)
class Solution:
def findSubstring(self, s, words):
"""
:type s: str
:type words: List[str]
:rtype: List[int]
"""
res = []
import copy
if len(words) == 0:
return res
wLen = len(words[0])
origD = {} # cannot use set(), since words might have duplicates
for w in words:
self.add2Dict(w,origD)
for i in range(wLen):
D = copy.deepcopy(origD)
foundD = {}
# check every possible starting position, to see whether it is a match
startIndex = i # the index that not valid
for end in range(i, len(s) - wLen +1, wLen):
# continue adding, till we find a match
substr = s[end:end+wLen]
if substr in D: # in original dict
self.add2Dict(substr,foundD)
self.removeD(substr,D)
if len(D) == 0:
res.append(startIndex)
topStr = s[startIndex: startIndex + wLen]
self.add2Dict(topStr, D)
self.removeD(topStr,foundD)
startIndex += wLen
elif substr in foundD: # found, but have been removed before
while s[startIndex: startIndex+ wLen] != substr:
ss = s[startIndex: startIndex+wLen]
self.add2Dict(ss,D)
self.removeD(ss,foundD)
startIndex += wLen
startIndex += wLen
else: # not in words at all, reset the whole
startIndex = end + wLen # next position is a good (or loop till right)
self.addD1toD2(foundD,D)
return res
def removeD(self, word, D):
if D[word] == 1:
del D[word]
else:
D[word] = D[word] -1
def add2Dict(self, word, D):
if word not in D:
D[word] = 1
else:
D[word] = D[word] + 1
def addD1toD2(self, D1, D2): # merge two dict, into D2
for k in D1:
self.add2Dict(k,D2)
D1.clear()
------------------------------4th pass----------------------------
思路: 因为允许重复,所以要记录下出现的次数,因此需要用HashMap.
而这次,为了加速,我们在最外边的循环中,用了 基于各个起始点的位置。
public class Solution {
public List<Integer> findSubstring(String S, String[] L) {
List<Integer> list = new LinkedList<Integer>();
if(S==null || L == null | S.length()==0 || L.length == 0)
return list;
// note that L might contains duplicates
int n = S.length(), m = L[0].length(), k = L.length;
HashMap<String, Integer> origMap = new HashMap<String,Integer>();
for(int i= 0;i<L.length;i++){
String str = L[i];
int count = 1;
if( origMap.containsKey(str)){
count += origMap.get(str);
}
origMap.put(str,count);
}
for(int i =0;i<m;i++){ // for each possible starting position
HashMap<String,Integer> tmpMap = new HashMap<String, Integer>();
int j = i;
while( j<= n-m ){
// remove the oldest
if( j-m*k>=0 ){
String oldStr = S.substring(j-m*k,j-m*k+m);
if(tmpMap.containsKey(oldStr)){
if(tmpMap.get(oldStr)>1){
tmpMap.put(oldStr,tmpMap.get(oldStr)-1);
}else{
tmpMap.remove(oldStr);
}
}
}
// add the newest
String newStr = S.substring(j,j+m);
int nFind = 1;
if(tmpMap.containsKey(newStr)){
nFind += tmpMap.get(newStr);
}
tmpMap.put(newStr,nFind);
// check if it is match
boolean isAllFind = true;
for(String str : origMap.keySet()){
if(tmpMap.containsKey(str)== false || tmpMap.get(str)!=origMap.get(str)){
isAllFind = false;
break;
}
}
if(isAllFind)
list.add(j-m*k+m);
//update j
j+=m;
}
}
return list;
}
}
--------------------第二遍----------- AC 了 --------
思路就是: 既然允许有重复, 而且, 还要尽量快, 用Set是不太行了。 那就用HashMap, 来记录下 每个key的出现的次数。
public class Solution {
public ArrayList<Integer> findSubstring(String S, String[] L) {
ArrayList<Integer> al = new ArrayList<Integer>();
if (S == null || L == null || S.length() == 0 || L.length == 0)
return al;
// make a set from L, so that we can
int nL = L.length;
int mL = L[0].length();
for (int k = 0; k <= S.length() - nL * mL; k++) {
Map<String, Integer> map = buildSet(L);
String curS = S.substring(k, k + mL * nL);
int i = 0;
int matchedCount = 0;
while (matchedCount < nL) {
String tmp = curS.substring(i, i + mL);
if (map.containsKey(tmp)) {
int count = map.get(tmp);
if(count <=0)
break;
map.put(tmp, count - 1);
i += mL;
matchedCount++;
} else
break;
}
// iterator all the list, to see whether it has been fully visited
boolean isAllFind = true;
for(String key : map.keySet())
if(map.get(key)>0){
isAllFind = false;
break;
}
if(isAllFind)
al.add(k);
}
return al;
}
private Map<String, Integer> buildSet(String[] L) {
Map<String, Integer> map = new HashMap<String, Integer>();
for (int i = 0; i < L.length; i++) {
if (map.containsKey(L[i])) {
int count = map.get(L[i]);
map.put(L[i], count + 1);
} else
map.put(L[i], 1);
}
return map;
}
}
Mistakes:
----------------第二遍------开始理解错题意了。
1: 所谓的withougt any interleaving characters,是指一个 concatenation里面不能interleaving. 但是,多个,是可以的。
2: 输入的 L 可以是["a","b","a"] , 因此,不能用Set 来存储。
Learned:
---------------3 rd 想利用每个L 中的 string 映射到一个HashMap上去。------------
思想很简单, 和楼上的那个通不过的方法如出一致。-------------其实就是第二遍的思想。-------
就是对每个string in L ,map 一个数值。 作为标记。 然后,看其是否在map中出现过,如果出现过,对比出现的次数。
但是,还是suffer了这样一个问题, 就是L 中有重复的情况。
因此,我们还需要记录在L中出现的次数。 作为参照物,还要记录用过的次数。
因此,我们自己定义了一个类。
public class Solution {
public ArrayList<Integer> findSubstring(String S, String[] L) {
ArrayList<Integer> al = new ArrayList<Integer>();
int n = L.length;// assume n > 0
int l = L[0].length();
HashMap<String, SNode> map = new HashMap<String, SNode>();
int nDifferent=0; // number of different Strings in L
for(int i =0;i< n;i++){
SNode node = map.get(L[i]);
if(node == null){
nDifferent++;
node = new SNode(nDifferent);
map.put(L[i],node);
}else
node.count++;
}
// for each position, calculate its total sum
for(int i =0;i<= S.length() - l*n; i++){
for(int j =0;j< n;j++) // reset all
map.get(L[j]).nUsed=0;
// test sum of these String
boolean isBreak = false;
for(int k = 0;k<n;k++){
String str= S.substring(i+k*l, i+(k+1)*l);
SNode node = map.get(str);
if(node == null || node.nUsed>=node.count){
isBreak = true;
break;
}else{
node.nUsed++;
}
}
if(!isBreak)
al.add(i);
}
return al;
}
}
class SNode{
public int val;
public int count;
public int nUsed=0; // number of used
public SNode(int v){
val = v;
count = 1;
}
}
No comments:
Post a Comment