题目链接

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

暴力解法,从字符串s的每个位置都判断一次(如果从当前位置开始的子串长度小于L中所有单词长度,不用判断),从当前位置开始的子串的前段部分能不能由集合L里面的单词拼接而成。

从某一个位置 i 判断时,依次判断单词s[i,i+2], s[i+3,i+5], s[i+6, i+8]…是否在集合中,如果单词在集合中,就从集合中删除该单词。

我们用一个hash map来保存单词,这样可以在O(1)时间内判断单词是否在集合中

算法的时间复杂度是O(n*(l*k))n是字符串的长度,l是单词的个数,k是单词的长度

递归代码如下:

class Solution {
private:
int wordLen; public:
vector<int> findSubstring(string S, vector<string> &L) {
unordered_map<string, int>wordTimes;
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]]++;
wordLen = L[0].size(); vector<int> res;
for(int i = 0; i <= (int)(S.size()-L.size()*wordLen); i++)
if(helper(S, i, wordTimes, L.size()))
res.push_back(i);
return res;
} //判断子串s[index...]的前段是否能由L中的单词组合而成
bool helper(string &s, const int index,
unordered_map<string, int>&wordTimes, const int wordNum)
{
if(wordNum == 0)return true;
string firstWord = s.substr(index, wordLen);
unordered_map<string, int>::iterator ite = wordTimes.find(firstWord);
if(ite != wordTimes.end() && ite->second > 0)
{
(ite->second)--;
bool res = helper(s, index+wordLen, wordTimes, wordNum-1);
(ite->second)++;//恢复hash map的状态
return res;
}
else return false;
}
};

非递归代码如下:

class Solution {
private:
int wordLen; public:
vector<int> findSubstring(string S, vector<string> &L) {
unordered_map<string, int>wordTimes;
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]]++;
wordLen = L[0].size(); vector<int> res;
for(int i = 0; i <= (int)(S.size()-L.size()*wordLen); i++)
if(helper(S, i, wordTimes, L.size()))
res.push_back(i);
return res;
} //判断子串s[index...]的前段是否能由L中的单词组合而成
bool helper(const string &s, int index,
unordered_map<string, int>wordTimes, int wordNum)
{
for(int i = index; wordNum != 0 && i <= (int)s.size()-wordLen; i+=wordLen)
{
string word = s.substr(i, wordLen);
unordered_map<string, int>::iterator ite = wordTimes.find(word);
if(ite != wordTimes.end() && ite->second > 0)
{ite->second--; wordNum--;}
else return false;
}
if(wordNum == 0)return true;
else return false;
}
};

OJ递归的时间小于非递归时间,因为非递归的helper函数中,hash map参数是传值的方式,每次调用都要拷贝一次hash map,递归代码中一直只存在一个hash map对象


算法2

回想前面的题目:LeetCode:Longest Substring Without Repeating CharactersLeetCode:Minimum Window Substring ,都用了一种滑动窗口的方法。这一题也可以利用相同的思想。

比如s = “a1b2c3a1d4”L={“a1”,“b2”,“c3”,“d4”}

窗口最开始为空,

a1在L中,加入窗口 【a1】b2c3a1d4                            本文地址

b2在L中,加入窗口 【a1b2】c3a1d4

c3在L中,加入窗口 【a1b2c3】a1d4

a1在L中了,但是前面a1已经算了一次,此时只需要把窗口向右移动一个单词a1【b2c3a1】d4

d4在L中,加入窗口a1【b2c3a1d4】找到了一个匹配

如果把s改为“a1b2c3kka1d4”,那么在第四步中会碰到单词kk,kk不在L中,此时窗口起始位置移动到kk后面a1b2c3kk【a1d4

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;
}
};

算法时间复杂度为O(n*k))n是字符串的长度,k是单词的长度

【版权声明】转载请注明出处http://www.cnblogs.com/TenosDoIt/p/3807055.html

LeetCode:Substring with Concatenation of All Words (summarize)的更多相关文章

  1. 【LeetCode】647. Palindromic Substrings 解题报告(Python)

    [LeetCode]647. Palindromic Substrings 解题报告(Python) 标签: LeetCode 题目地址:https://leetcode.com/problems/p ...

  2. Leetcode之回溯法专题-78. 子集(Subsets)

    Leetcode之回溯法专题-78. 子集(Subsets) 给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集). 说明:解集不能包含重复的子集. 示例: 输入: nums = ...

  3. Leetcode之回溯法专题-77. 组合(Combinations)

    Leetcode之回溯法专题-77. 组合(Combinations)   给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合. 示例: 输入: n = 4, k = 2 输 ...

  4. Leetcode之回溯法专题-46. 全排列(Permutations)

    Leetcode之回溯法专题-46. 全排列(Permutations) 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3, ...

  5. 【LeetCode】376. Wiggle Subsequence 解题报告(Python)

    [LeetCode]376. Wiggle Subsequence 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.c ...

  6. 【LeetCode】649. Dota2 Senate 解题报告(Python)

    [LeetCode]649. Dota2 Senate 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地 ...

  7. 【LeetCode】911. Online Election 解题报告(Python)

    [LeetCode]911. Online Election 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ ...

  8. 【LeetCode】886. Possible Bipartition 解题报告(Python)

    [LeetCode]886. Possible Bipartition 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu ...

  9. 【LeetCode】36. Valid Sudoku 解题报告(Python)

    [LeetCode]36. Valid Sudoku 解题报告(Python) 作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址 ...

随机推荐

  1. 专题:Channel Bonding/bonding

    EtherChannel最初是由cisco提出,通过聚合多条物理链路为单条逻辑链路,从而实现高可用及提高吞吐量等目的.AgP(Port Aggregation Protocol,Cisco专有协议). ...

  2. Til the Cows Come Home

    Description Bessie is out in the field and wants to get back to the barn to get as much sleep as pos ...

  3. wexinjs 调用

    public class Utils    {       static string appid = GetAppSettingValue("appid");       sta ...

  4. 10. Software, Software Engineering, water fall (瀑布模型),Code Complete等名词的来源

    ①.Software-软件”一词是20世纪60年代才出现的,软件Software——1958年由贝尔实验室的著名统计学家John Tukey 提出软件与硬件一起构成完整的计算机系统,它们是相互依存,缺 ...

  5. 4.“写程序” 这个活动大多数情况下是个人行为。 我们听说的优秀程序员似乎都是单打独斗地完成任务。同学们在大学里也认识一些参加ACM 比赛的编程牛人, 他们写的ACM 比赛的程序是软件么? “写程序” 和 ”做软件“ 有区别么? 请采访这些学生。

    ACM的题库的编程都只能算做程序,不能算软件.写程序和做软件区别还是很大的.程序是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合.为实现预期目的而进行操作的一系列语句和指令.而软件是程 ...

  6. android shape的使用

    文章出自:http://www.cnblogs.com/cyanfei/archive/2012/07/27/2612023.html <?xml version="1.0" ...

  7. 《LINUX内核设计与实现》读书笔记之第一章和第二章

    一.第一章 1. Unix内核的特点简洁:仅提供系统调用并有一个非常明确的设计目的抽象:几乎所有东西都被当做文件可移植性:使用C语言编写,使得其在各种硬件体系架构面前都具备令人惊异的移植能力进程:创建 ...

  8. Winpcap构建用户级网桥

    Winpcap构建网桥 根据winpcap sdk中的user-level-bridge用户级网桥 |机器1                 |                  |机器2   | | ...

  9. How to change drive in cygwin

    In DOS you may have been used to D: to change to the D drive. Cygwin provides a mapping of DOS/Windo ...

  10. PHP微信墙制作,开源

    PHP微信墙制作 微信墙 PHP 注意:由于微信官网不定时会更新,其中模拟登陆以及爬取数据的方式可能会失效,最近这12个月里,就有两次更新导致此功能需要重写. 服务端源码->github地址传送 ...