【題目】
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存在一個map中,key是單詞字符串,value是出現的次數。然後逐個位置遍歷字符串s(注意遍歷結束位置不必到最後,剩餘長度小於單詞總長度即停止),判斷其後面的和檔次總長度相同的子串中的每個單詞是否和words一樣。如果一樣,這push_back進去;否則,遍歷下一個字符。時間複雜度:O(LNw),L是s的長度,N是words的個數,w是word的長度。
代碼:
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector <int> res;
int N = words.size(); // number of words
if(N == 0)
return res;
int len = words[0].length(); // length of each word
int strLen = s.length(); // length of string
map<string, int> countWords; // get the wordCount map
for(int i = 0; i < N; i ++)
{
if(countWords.count(words[i]))
countWords[words[i]]++;
else
countWords[words[i]] = 1;
}
map<string, int> counting;
for(int i = 0; i <= strLen - len * N; i++) // first loop
{
counting.clear();
bool flag = true;
for(int j = i; j < i + N*len; j += len) // second loop
{
string w = s.substr(j, len);
if(countWords.count(w) == 0) // if not exist in countWords, break directly
{
flag = false;
break;
}
else
{
if(counting.count(w)) // if not new
counting[w]++;
else
counting[w] = 1;
}
}
if(!flag)
continue;
else
{
if(compare(counting, countWords))
res.push_back(i);
else
continue;
}
}
}
bool compare(map<string, int> counting, map<string, int> countWords)
{
map<string, int>::iterator iter;
for(iter = countWords.begin(); iter != countWords.end(); iter++)
{
if(counting[iter->first] != iter->second)
return false;
}
return true;
}
};
解法二:窗口移動法。
首先,初始化一個長度爲0的窗口,定義頭部是begin,尾部是tail。判斷tail後面的一個單詞,如果在words裏面,則tail往後移動,即窗口伸長一個單詞的量;如果tail後面的單詞壓根兒不在words裏面,那麼把begin後移到最後位置,重新初始化窗口,繼續判斷;如果tail後面的單詞是之前出現過,不過words中沒有這個單詞的容量,那麼begin後移到該單詞第一次出現的位置,繼續判斷。這個方法的時間複雜度爲O(Lw)。
代碼:
class Solution {
public:
vector<int> findSubstring(string S, vector<string> &L) {
unordered_map<string, int>wordTimes;//L中單詞出現的次數
for(int i = 0; i < L.size(); i++)
if(wordTimes.count(L[i]) == 0)
wordTimes.insert(make_pair(L[i], 1));
else wordTimes[L[i]]++;
int wordLen = L[0].size();
vector<int> res;
for(int i = 0; i < wordLen; i++)
{//爲了不遺漏從s的每一個位置開始的子串,第一層循環爲單詞的長度
unordered_map<string, int>wordTimes2;//當前窗口中單詞出現的次數
int winStart = i, cnt = 0;//winStart爲窗口起始位置,cnt爲當前窗口中的單詞數目
for(int winEnd = i; winEnd <= (int)S.size()-wordLen; winEnd+=wordLen)
{//窗口爲[winStart,winEnd)
string word = S.substr(winEnd, wordLen);
if(wordTimes.find(word) != wordTimes.end())
{
if(wordTimes2.find(word) == wordTimes2.end())
wordTimes2[word] = 1;
else wordTimes2[word]++;
if(wordTimes2[word] <= wordTimes[word])
cnt++;
else
{//當前的單詞在L中,但是它已經在窗口中出現了相應的次數,不應該加入窗口
//此時,應該把窗口起始位置想左移動到,該單詞第一次出現的位置的下一個單詞位置
for(int k = winStart; ; k += wordLen)
{
string tmpstr = S.substr(k, wordLen);
wordTimes2[tmpstr]--;
if(tmpstr == word)
{
winStart = k + wordLen;
break;
}
cnt--;
}
}
if(cnt == L.size())
res.push_back(winStart);
}
else
{//發現不在L中的單詞
winStart = winEnd + wordLen;
wordTimes2.clear();
cnt = 0;
}
}
}
return res;
}
};
另外還有一個代碼,沒有調試成功,後續修改ing。
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
vector <int> res;
int N = words.size(); // number of words
if(N == 0)
return res;
int len = words[0].length(); // length of each word
int strLen = s.length(); // length of string
map<string, int> countWords; // get the wordCount map
for(int i = 0; i < N; i ++)
{
if(countWords.count(words[i]))
countWords[words[i]]++;
else
countWords[words[i]] = 1;
}
map<string, int> counting = countWords;
int begin = 0, tail = begin;
while(tail < strLen)
{
if(tail - begin + 1 == N * len) // get results
{
res.push_back(begin);
begin = tail + 1;
tail = begin;
counting = countWords;
continue;
}
string w = s.substr(tail, len);
int kind = moveClass(w, counting); // get the manner how to move the tail
if(kind == 1) // if w not in countWords
{
begin = tail + 2;
tail = begin;
counting = countWords;
continue;
}
if(kind == 2) // if w in countWords now
{
tail += len;
continue;
}
if(kind == 3) // if w in countWords in past
{
string tmp = s.substr(begin, tail - begin + 1);
begin = tmp.find(w) + 1;
tail = begin;
counting = countWords;
continue;
}
}
return res;
}
int moveClass(string w, map<string, int>& counting)
{
if(counting.count(w) == 0)
return 1;
else if(counting[w] >= 1)
{
counting[w]--;
return 2;
}
else
return 3;
}
};