class Solution
{
public:
vi findSubstring(string s,vector<string>& L)
{
if(s.empty()||L.empty())
return vi();
int len_word=L[0].length();
set<int> retS;
for(int i=0;i<len_word;i++)
{
string t=string(&s[0+i],&s[0]+s.length());
_findSubstring(t,L,retS,i);
}
vi ret(retS.begin(),retS.end());
return ret;
}
void _findSubstring(string s,vector<string>& L,set<int>& ret,int off)
{
int len_word=L[0].length();
int sz=L.size();
//assert(s.length()%len_word==0);
int word_num=s.length()/len_word;
if(word_num==0)
return ;
int i,j;
vector<string> words;
vector<int> wordsTime;
words.push_back(L[0]);
wordsTime.push_back(1);
for(i=1;i<sz;i++)
{
for(j=0;j<i;j++)
{
if(L[i].compare(L[j])==0)
{
wordsTime[j]++;
break;
}
}
if(j==i)
{
words.push_back(L[i]);
wordsTime.push_back(1);
}
}
int wsz=words.size();
vi indexs;
indexs.reserve(word_num);
for(i=0;i<word_num;i++)
{
string t=string(&s[len_word*i],&s[len_word*i]+len_word);
int idx=-1;
for(j=0;j<wsz;j++)
{
if(words[j].compare(t)==0)
{
idx=j;
break;
}
}
indexs.push_back(idx);
}
vi emerge(sz,0);
int exist=0;
int left=0;
for(i=0;i<word_num;i++)
{
if (indexs[i]==-1)
{
for(j=left;j<i;j++)
{
emerge[indexs[j]]--;
exist-=1;
}
left=i+1;
}
else if (emerge[indexs[i]]==wordsTime[indexs[i]])
{
emerge[indexs[i]]=-1;
for(j=left;emerge[indexs[j]]!=-1;j++)
{
emerge[indexs[j]]--;
exist--;
}
emerge[indexs[i]]=wordsTime[indexs[i]];
left=j+1;
}
else
{
emerge[indexs[i]]++;
exist++;
if (exist==sz)
{
ret.insert(len_word*left+off);
emerge[indexs[left]]--;
exist--;
left++;
}
}
}
}
};
题目如下:
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).
在编程之美上有一个题与此类似,此题不同在于
1.L中每个单词只能出现一次
2.单词间必须是连续的
注意L中的单词可以有重复的,如L[ "a","b","a"],所以统计每个单词应当出现的次数的时候,要考虑重复;
另外很恶心的是待检查的是个字符串而不是一个数组,我采用的方法是首先在s[0]开头的子串中检查;然后是s[1]。。。可知这样的效率实在很差,尤其是L中单词的长度很长的时候。
如果您有更好的方法,希望能告知我~
解题思想:
1.首先为L中数组建立希望的重复次数数组words_time,同时去除掉重复的单词words,这部分的复杂度是O( len(L[0]) * len( L[0]) );
2.将待检查的子串S按L[0]的长度分成K ( K=len(S) / len(L[0]) )个等长单词,同时建立S中单词在words中的下标,这部分的复杂度是 O( K * len(L[0] ) );
3.枚举S' ( S ' 是原S去掉开头的子串) 中的每一个单词i,同时维护当前已包含单词的范围最左单词left,检查单词i 的出现情况:
3.1 如果该单词根本不属于words,按题意已经得到的这个范围的所有单词都无效;
3.2 如果该单词已经出现到需要的次数,应当擦去范围中该单词第一次出现及之前出现的所有单词;
3.3 如果该单词还可以出现,检查是否满足输出条件;
因为预处理建立的indexs数组使得第三步擦去每个无效单词可在常数时间完成,所以这部分的复杂度是 O ( K ) ;
如前面提到,必须对 S[0]、S[1].......S[ len(L[0]) -1] 依次调用上述过程,所以总的复杂度是 O(len(L[0]) * ( len( L[0] )^2 + K* len(L[0]) + K ) ;
代码较乱较丑陋,有时间再改吧。