每日算法之二十六:Substring with Concatenation of All Words

变相的字符串匹配

给定一个字符串,然后再给定一组相同长度的单词列表,要求在字符串中查找满足以下条件的起始位置:

1)从这个位置开始包含单词列表中所有的单词,且每个单词仅且必须出现一次。

2)在出现的过程中不能出现其他的干扰单词。

3)出现的位置可能有多个。

4)单词的出现顺序不做要求。

下面是一个例子:

S:"barfoothefoobarman"

L:"foo","bar"

位置0是出现位置,;两个单词均出现仅出现一次,且没有干扰。同样位置9也是满足的。

原题要求如下:

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.

思路如下:

首先初始化一个map容器,内含单词列表中出现的单词,以及单词出现的次数。把容器当做比较的模板。

string int
bar 1
foo 1
其次,指针i从0指向可能的最后位置,上例中是12,因为单词列表中总长度是6,在S中留下足够6的长度位置即可。因为中间干扰词的长度是不确定的,因此i只能是逐一的后移来寻找匹配的位置。

我们首先获得字符串中的第一个单词,bar。查看在上述的容器中是否有这个单词,如果没有,直接指针后移匹配下一个位置开始的单词。查找后是有的,因此我们把这个单词加入到一个新的map容器中,这个容器存储的是从当前指针位置开始满足单词列表的单词,这这个例子中就是bar在初始化的容器中存在,那么就把他加入新容器中。同时,统计次数也要递增,接下来查看这个单词在新容器中出现的次数是否小于等于初始化容器中的次数。如果大于说明这是错误的,也需要指针后移。

最后,当我们后移单词列表中指定个数的单词或者因为不匹配而终止从指针位置i开始的查找时,在循环外面我们要判断一下单词匹配的个数是否和单词列表中的次数一样,如果一样说明从当前指针的位置是匹配的。那么就把这个指针位置保存起来。如此循环往复即可。代码如下,顺着走一遍就明白了。

class Solution {
public:
    vector<int> findSubstring(string S, vector<string> &L) {
        map<string,int> words,cur;
        int wordNum = L.size();
        int wordLen = L[0].size();
        vector<int> res;
        for(int k = 0;k<wordNum;k++)
          words[L[k]]++;//初始化容器
        for(int i = 0;i<=static_cast<int>(S.length()-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(words[word]<cur[word])//出现的次数多了
              break;

          }
          if(j == wordNum)//这时候是匹配的
            res.push_back(i);
        }
        return res;
    }
};



你可能感兴趣的:(每日算法之二十六:Substring with Concatenation of All Words)