一看这道题就知道不是一个简单的题,不想一下子就放弃,就写了写,第一个的想法是找每个字符串出现的位置,然后判断位置是否连续(既包含所有的字符串),但是没有用map,也没有记录每个单词出现的次数,结构之混乱,思维之复杂,弄了好久调通了,时间限制。烂代码贴上也无妨,不要学就是了。
vector<int> findSubstring(string S, vector<string> &L) {
int lenL = L.size();
int lenstr;
vector<int> ret;
if (lenL==0)
return ret;
else
lenstr = L[0].size();
int *pos = new int[lenL];
memset(pos,0,sizeof(int)*lenL);
vector<int> st;
bool flag =true;
while(flag)
{
for (int i=0;i<lenL;i++)
{
bool flag2 = true;
while(flag2)
{
pos[i] = S.find(L[i],pos[i]);// return -1 when doesn't exist
if (pos[i] == -1)
{
flag = false;
break;
}
flag2 = false;
for (int j=0;j<i;j++)
{
if(pos[j]-lenstr == pos[i])
{
pos[i] += lenstr;
flag2 = true;
}
}
}
if (flag == false)
break;
st.push_back(pos[i]);
pos[i] += lenstr;
}
if (flag == false)
break;
//vector<int> stcopy(st);
sort(st.begin(),st.end());
int i;
for (i=1;i<st.size();i++)
{
if (st[i-1]+lenstr != st[i])
{
break;
}
}
if (i==st.size())
{
ret.push_back(st[0]);
st.clear();
}
else
{
if (i>0&&st[i] >= st[i-1]+lenstr)
{
for (int j=0;j<lenL;j++)
{
if (pos[j]>=st[i])
{
pos[j] -= lenstr;
}
}
}
else
{
int min=1000,k=0;
for (int j=0;j<lenL;j++)//find the least p
{
if (min>pos[j])
{
k = j;
min = pos[j];
}
}
for (int j=0;j<lenL;j++)
{
if (pos[j]>pos[k])
{
pos[j] -= lenstr;
}
}
}
st.clear();
}
}
delete[] pos;
return ret;
}
第二次想法,没有用find,用了map记录次数,不断判断下一个字符是不是给定的字符串数组里面的,然后一次移动一个位置。还是时间限制。感觉和正确的暴力算法没差多少,怎么就通不过呢?代码如下:
vector<int> findSubstring1(string S, vector<string> &L) {
int lenL = L.size();
int lenstr;
vector<int> ret;
if (lenL==0)
return ret;
else
lenstr = L[0].size();
map<string,int> mpo;
int allsum = lenstr*lenL;
if (allsum>S.length())
{
return ret;
}
for (int i=0;i<lenL;i++)
{
mpo[L[i]]++;
}
for (int i=0;i<S.length();i++)
{
map<string,int> mp(mpo);
int j=i;
bool flag = true;
while(j<S.length())
{
map<string,int>::iterator iter = mp.begin();
int pos = -1;
while (iter != mp.end())
{
if(S.substr(j,lenstr) ==iter->first)
{
if (mp[iter->first]>0)
{
j += lenstr;
mp[iter->first]--;
if (j-i == allsum)
{
ret.push_back(i);
i = i + lenstr - 1;//j-1;//i += sum-1;//here,i move
flag = false;
break;
}
}
else
{
flag = false;
break;
}
iter = mp.begin();
}
else
{
iter++;
}
}
if (iter == mp.end()||flag == false)
break;
}
}
return ret;
}
下面的两个算法,一个比一个牛逼,真是见识了。下面的暴力算法,思维之缜密,结构之清晰,令我佩服的五体投地。而且这题最让我感觉头疼的就是程序的结构,逻辑之间的关系,水平高低一下子就看出来了。同样的想法,能工整清晰的实现出来就是不一般的。有两点和上面的程序是不一样的,一是它用另一个map来记录找到的次数,来判断是不是超过了需要的字符串,二是他用j从零开始,只是作为个数,不作为索引。这样可以用j是否到个数来判断是否找到结果。但整体上比我的程序清晰多了。真是简单、粗暴、美!如下:
vector<int> findSubstring3(string S, vector<string> &L) { //1632ms
// Note: The Solution object is instantiated only once.
map<string,int> words;
map<string,int> cur;
int wordNum = L.size();
for(int i = 0; i < wordNum; i++)
words[L[i]]++;
int wordLen = L[0].size();
vector<int> res;
//if(S.size() < wordLen*wordNum)return res;
for(int i = 0; i <= (int)S.size()-wordLen*wordNum; i++)
{
cur.clear();
int j;
for(j = 0; j < wordNum; j++)
{
string word = S.substr(i+j*wordLen, wordLen);
if(words.find(word) == words.end())
break;
cur[word]++;
if(cur[word]>words[word])
break;
}
if(j == wordNum)
res.push_back(i);
}
return res;
}
下面的想法说是来自leetCode上的讨论,思路也很清晰,明了。我在这看的
http://blog.youkuaiyun.com/kenden23/article/details/17054283 思路也是:先记录位置(和我最开始的想法一样),然后判断位置是否连续。不过比我那高级多了,先是记录位置时就已经区分了不同的单词位置标记不一样;然后用need和found来表示是否找到,找到多少,差多少等;然后用了窗口的思想,不断的像后移动窗口,一次一个固定长度(之前还要确定步长)。看倒是能看明白,写我写不出来,一年年后我能写出这样的代码就满足了。
vector<int> findSubstring(string S, vector<string> &L) { //226ms
// Start typing your C/C++ solution below
// DO NOT write int main() function
int m = S.size(), n = L.size(), len = L[0].size();
map<string, int> ids;
vector<int> need(n, 0);
for (int i = 0; i < n; ++i) {
if (!ids.count(L[i])) ids[L[i]] = i;
need[ids[L[i]]]++;
}
vector<int> s(m, -1);
for (int i = 0; i < m - len + 1; ++i) {
string key = S.substr(i, len);
s[i] = ids.count(key) ? ids[key] : -1;
}
vector<int> ret;
for (int offset = 0; offset < len; ++offset) {
vector<int> found(n, 0);
int count = 0, begin = offset;
for (int i = offset; i < m - len + 1; i += len) {
if (s[i] < 0) {
// recount
begin = i + len;
count = 0;
found.clear();
found.resize(n, 0);
} else {
int id = s[i];
found[id]++;
if (need[id] && found[id] <= need[id])
count++;
if (count == n)
ret.push_back(begin);
// move current window
if ((i - begin) / len + 1 == n) {
id = s[begin];
if (need[id] && found[id] == need[id])
count--;
found[id]--;
begin += len;
}
}
}
}
return ret;
}
还有一个方法,比上面的方法更快一点,算是改进了,不过思路没有上面的清晰。下面的之所以快一点,是因为在找到的字符串数量超过需要的时候,上面的算法直接让i走到下一个点(即i加一个步长),而下面的算法却增加了一步,寻找最后的超过数量的字符串位置,i修正为第一个这个字符串(超过数量的那个)出现的位置,这样i一次走的更长了。这算法算是完美了吧,但是对艺术的追求是没止境的,当然了,也没准有更好的,不过对于我这样的菜鸟能看明白,并且时隔多日还能记得一点点就算不错了,也算是对得起大牛们的杰作。
vector<int> findSubstring5(string S, vector<string> &L)
{
int sLen = S.length();
int lLen = L.size();
if (lLen < 1) return vector<int>();
int len = L[0].length();
vector<int> res;
map<string, int> LMap;
map<string, int> countMap;
string index;
for (int i=0;i<lLen;i++)
LMap[L[i]]++;
//熟记这个思维,巧妙地分段
for (int d = 0; d < len; d++)
{
//注意:是i=d不是i=0;
for (int i = d; i <= sLen-lLen*len; i += len)
{
int start = i;
index = S.substr(i,len);
//注意:后面的是i<=sLen-len不是sLen-lLen*len
while (LMap.count(index) > 0 && i <= sLen-len)
{
countMap[index]++;
//这里就不能用map的count函数了,因为count只会返回0或1
//一定要熟悉STL函数的各种用法,否则会出错。
//这个函数把握不好,结果浪费大量的时间。
//本题卡的最长时间的是这个小小的函数和程序结构开始没写好
if (countMap[index] > LMap[index])
{
string sTemp = S.substr(start,len);
while (sTemp != index)
{
start += len;
sTemp = S.substr(start,len);
//每前进一步,减少一个相等元素
countMap[sTemp]--;
}
countMap[index]--;
start += len;
}
i+=len;
if ((i-start)/len == lLen)
{
res.push_back(start);
string sTemp = S.substr(start, len);
countMap[sTemp]--;
start+=len;
}
index = S.substr(i,len);
}
countMap.clear();
}
}
return res;
}
最后提出一个问题,也是最重要的问题,就是:为什么后面的方法会比前面的快那么多呢?