Substring with Concatenation of All Words 题解

题意

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 words exactly 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).

Subscribe to see which companies asked this question

大概来说,就是给定一串字符串和单词数组,找到字符串中,也就是子串必须全部包含单词数组中的单词,要求必须连续,顺序可以不要求,其中单词数组中的单词的个数是固定的,还有单词可以是重复的;

思路

其实最简单的思路就是对字符串进行逐次遍历,先找到第一个匹配的单词,这又要去往单词数组中去遍历,也就是其复杂时间为(字符串的长度*单词数组的单词的个数),虽然这种方法较为简单,但是其实花销是比较大的,同时需要注意的地方也是比较多的。所以在我参考一些代码之后,发现一些好的方法-包括双map,使用队列,使用trie树等等;

实现

我的实现(最简单容易理解)

vector<int> findSubstring1(string s, vector<string>& words) {vector<int> result;size_t word_len = words[0].length();multimap<string, bool> maps;for (size_t j = 0; j < words.size(); j++) {maps.insert(make_pair(words[j], false));}for (size_t i = 0; i < s.length(); i++) {for (size_t j = 0; j < words.size(); j++) {for (auto beg = maps.lower_bound(words[j]), end = maps.upper_bound(words[j]); beg != end; ++beg) {beg->second = false;}}//先找到第一个单词在子串中的位置string subs = s.substr(i, word_len);size_t first_pos = -1;for (size_t j = 0; j < words.size(); j++) {if (words[j] == subs) {first_pos = i;auto item = maps.find(words[j]);item->second = true;}}//找第一个单词以后的所有单词,如果成功则返回开始的下标if (first_pos != -1) {size_t last_pos = first_pos + words.size() * word_len;bool isValid = true;size_t k = first_pos + word_len;for (; k < last_pos; k+=word_len) {if (k + word_len > s.length()) {isValid = false;break;}string osubs = s.substr(k, word_len);auto item = maps.find(osubs);auto itemcnt = maps.count(osubs);if (item != maps.end()) {if (item->second == false) {item->second = true;}else if (itemcnt > 1) {bool ishave = false;for (auto beg = ++item, end = maps.upper_bound(item->first); beg != end; ++beg) {if (!beg->second) {beg->second = true;ishave = true;break;}}// 全部已经访问过了if (!ishave) {isValid = false;}}else if (itemcnt == 1) {isValid = false;}}else {isValid = false;}}// 坐标位置不正确,不成功if (k != last_pos) {isValid = false;}//没有全部访问过,不成功for (size_t q = 0; q < words.size(); q++) {for (auto beg = maps.lower_bound(words[q]), end = maps.upper_bound(words[q]); beg != end; ++beg) {if (!beg->second) {isValid = false;break;}}}//成功则加入结果中if(isValid) {result.push_back((int)first_pos);}}}return result;
}

双map(最基础的优化)

/***  默认的简化的方法,利用unorder_map进行判断,维护一个left值*  也就是全部单词字符串开始的地方**  @param s     <#s description#>*  @param words <#words description#>**  @return <#return value description#>*/
vector<int> findSubstring2(string s, vector<string>& words) {vector<int> ans;int n = s.size(), cnt = words.size();if (n <= 0 || cnt <= 0) {return ans;}// 单词的hash数组,初始化unordered_map<string, int> dict;for (int i = 0; i < cnt; ++i) dict[words[i]]++;int wl = words[0].length();for (int i = 0; i < wl; ++i) {// left为起始单词串的下标int left = i, count = 0;unordered_map<string, int> tdict;for (int j = i; j <= n - wl; j+=wl) {string str = s.substr(j, wl);// 计算单词数组中是否存在if (dict.count(str)) {tdict[str]++;// 计算已访问的单词个数if (tdict[str] <= dict[str]) {count++;}else {// 字符串中存在连续相同的单词,并且已经大于了单词数组中的个数,// 这时需要向右进行移动while (tdict[str] > dict[str]) {string str1 = s.substr(left, wl);tdict[str1]--;if (tdict[str1] < dict[str1]) {count--;}left += wl;}}//如果访问个数相同,则成功if (count == cnt) {ans.push_back(left);tdict[s.substr(left, wl)]--;count--;left += wl;}}else {// 失败,重新统计count = 0;tdict.clear();left += wl;}}}return ans;
}

使用队列

/***  这个方法比较复杂,比较难想懂,*  利用每个单词对应一个队列,并且队列中存储每个单词出现的下标(初始情况均为-1)*  根据下标去判断该单词的访问情况,或者第一次访问(-1),或者第n次访问(下标)等等*/
typedef unordered_map<string, queue<int>> wordItr;
vector<int> findSubstring3(string s, vector<string>& words) {vector<int> res;if (words.size() == 0)return res;if (s.length() == 0)return res;int wordlen = words[0].size();if (s.size() < wordlen) return res;wordItr wordHash;wordItr::iterator it;queue<int> q;q.push(-1);// 对哈希表进行初始化,存在则往队列中添加-1for (int i = 0; i < words.size(); i++) {it = wordHash.find(words[i]);if (it == wordHash.end()) {wordHash[words[i]] = q;}else {it->second.push(-1);}}wordItr temp = wordHash;for (int i = 0; i < wordlen; i++) {int curWordCnt = 0; //已经访问单词的个数wordHash = temp;for (int j = i; j <= s.size() - wordlen; j += wordlen) {string str = s.substr(j, wordlen);it = wordHash.find(str);// 哈希数组里面是否存在字符串的keyif (it == wordHash.end()) {curWordCnt = 0;}else {// 访问队列int lastPos = it->second.front();// 如果为-1则表明第一次访问该单词if (lastPos == -1) {curWordCnt++;}// ??else if (curWordCnt * wordlen < j - lastPos) {curWordCnt++;}// 在访问完一次所有单词以后,重复出现该单词,该位置已经发生变化else {curWordCnt = (j - lastPos)/wordlen;}it->second.pop();it->second.push(j); //该单词出现的下标// 测试...queue<int> tque = it->second;while (!tque.empty()) {cout << it->first << "->" << tque.front();tque.pop();}cout << endl;// 当前访问单词个数已经访问完if (curWordCnt == words.size()) {res.push_back((int)(j - wordlen * (words.size() - 1)));}}}}return res;
}

Trie树

/***  这个方法可能更难想到,因为是用的trie树,*  相较于前面的哈希,这里使用trie树进行适配**  @param s     <#s description#>*  @param words <#words description#>**  @return <#return value description#>*/
class TrieNode {
public:TrieNode* child[26];int cnt;TrieNode(): cnt(0) {memset(child, NULL, sizeof(TrieNode*) * 26);//分配空间}
};class Trie {TrieNode* root;
public:Trie() {root = new TrieNode();}TrieNode* getRoot() {return root;}void buildTrie(vector<string> words) {for (string word : words) {addWord(word);}}void addWord(string& word) {TrieNode* cur = root;for (int i = 0; i < word.size(); i++) {char m = word[i] - 'a';if (!cur->child[m]) {cur->child[m] = new TrieNode();}cur = cur->child[m];}cur->cnt++;}
};Trie* trie;
/***  利用递归将字符串中的所有单词用trie树进行查找,找不到则表明不符合*  我觉得除了递归以外,也可以通过两个遍历,最外层为遍历单词的个数,移动单词长度,*  最内层循环为对每一个单词的进行Trie树的匹配;**  @param s     <#s description#>*  @param start <#start description#>*  @param end   <#end description#>**  @return <#return value description#>*/
bool isSubString1(string& s, int start, int end) {TrieNode* node = trie->getRoot();int idx;for (int i = start; i < end; i++) {idx = s[i] - 'a';if (!node->child[idx]) {return false;}node = node->child[idx];// 表明已经达到单词的末尾if (node->cnt > 0) {node->cnt--; //标记为已经使用if (i + 1 == end || isSubString1(s, i+1, end)) {node->cnt++; //标记为未使用return true;}node->cnt++; //标记为未使用}}return false;
}/***  这个方法比较巧妙,利用trie树去匹配字符串中的所有单词**  @param s     <#s description#>*  @param words <#words description#>**  @return <#return value description#>*/
vector<int> findSubstring4(string s, vector<string>& words) {trie = new Trie();trie->buildTrie(words);int length = (int)words[0].size() * words.size();vector<int> result;for (int i = 0; i < s.length() - length; i++) {if (isSubString1(s, i, i+length)) {result.push_back(i);}}return result;
}

总结

我觉得无论是什么方法,都逃不掉对字符串的遍历,对单词的匹配,就是看这个过程可以进行多大的优化。

转载于:https://www.cnblogs.com/George1994/p/6272242.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/371690.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java udp丢包_linux 系统 UDP 丢包问题分析思路

最近工作中遇到某个服务器应用程序 UDP 丢包&#xff0c;在排查过程中查阅了很多资料&#xff0c;总结出来这篇文章&#xff0c;供更多人参考。在开始之前&#xff0c;我们先用一张图解释 linux 系统接收网络报文的过程。● 首先网络报文通过物理网线发送到网卡● 网络驱动程…

【SQL】分组数据,过滤分组-group by , having

学习笔记&#xff0c;原文来自http://blog.csdn.net/robinjwong/article/details/24845125 创建分组 - GROUP BY 分组是在SELECT语句的GROUP BY子句中建立的。它的作用是通过一定的规则将一个数据集划分成若干个小的区域&#xff0c;然后针对若干个小区域进行数据处理。SELECT子…

使用EasyMock或Mockito

我大部分时间都在使用EasyMock &#xff0c;但是最近我和一些非常愿意使用Mockito的人一起工作。 我不打算在同一项目中使用两个框架来实现相同的目的&#xff0c;因此我采用了Mockito 。 因此&#xff0c;在过去的几个月中&#xff0c;我一直在使用Mockito &#xff0c;这是我…

CentOS下iptables 配置详解

如果你的IPTABLES基础知识还不了解,建议先去看看. 开始配置 我们来配置一个filter表的防火墙. (1)查看本机关于IPTABLES的设置情况 [roottp ~]# iptables -L -nChain INPUT (policy ACCEPT)target prot opt source destination Chain FORWARD (policy…

java lambda 多个参数_Java Lambda行为参数化

Java Lambda行为参数化我们可以将lambda表达式作为参数传递给方法。例子以下代码创建了一个名为Calculator的函数接口。在Calculator中有一个称为calculate的方法&#xff0c;它接受两个int参数并返回一个int值。在Main类中有一个引擎方法&#xff0c;它接受函数接口Calculator…

具有关联映射的Hibernate Composite ID

最近&#xff0c;我们面临着带有复合id字段的休眠关联映射的棘手情况。 我们需要与一对一和多对一建立双向关联。我们的拖曳表是“ REPORT”和“ REPORT_SUMMARY”&#xff0c;它们之间具有从REPORT到REPORT_SUMMARY的一对多关系&#xff0c;而从REPORT_SUMMARY到REPORT表。 RE…

ZooKeeper应用场景

ZooKeeper是一个高可用的分布式数据管理与系统协调框架。基于对Paxos算法的实现&#xff0c;使该框架保证了分布式环境中数据的强一致性&#xff0c;也正是基于这样的特性&#xff0c;使得ZooKeeper解决很多分布式问题。网上对ZK的应用场景也有不少介绍&#xff0c;本文将结合作…

java main test_java调用main自动执行testng方法一

主方法import com.com.utlis.createTestngXml;import org.testng.TestNG;import java.util.ArrayList;import java.util.List;/*** author lw* createTime 2019/3/30 15:35* description testng调试类*/public class inittet {private static final String ESCAPE_PROPERTY &q…

有趣的Ruby-学习笔记3

Ruby方法方法名要以小写字母开头。假设用大写字母开头会被作为常量 &#xff08;这点非常奇怪&#xff09;定义一个无參的方法def method_name expr.. end定义一个有參的方法def method_name (var1, var2)expr.. end给參数设定默认值def method_name (var1value1, var2value2)e…

ADF:动态视图对象

今天&#xff0c;我想写有关动态视图对象的文章&#xff0c;它允许我在运行时更改其数据源&#xff08;SQL查询&#xff09;和属性。 我将使用oracle.jbo.ApplicationModule :: createViewObjectFromQueryStmt方法来解决此问题。 我将逐步介绍如何执行此操作 创建视图对象和应…

java stack list_JAVA自己实现List接口Stack

package 集合.Stack;import java.util.Arrays;import java.util.EmptyStackException;import java.util.Vector;public class MyStack {//底层数组默认长度为10private Object[] myStack new Object[10];//sizeprivate int size 0;public MyStack() {}//pushpublic Object pu…

刷题总结——序列操作(权值线段树套树状数组)

题目&#xff1a; 题目描述 给出序列 a1&#xff0c;a2&#xff0c;…&#xff0c;an&#xff08;0≤ai≤109&#xff09;&#xff0c;有关序列的两种操作。 1. ai&#xff08;1≤i≤n&#xff09;变成 x&#xff08;0≤x≤109&#xff09;。 2. 求 al&#xff0c;al1&#xff…

原型模式Prototype

原型模式 http://www.cnblogs.com/zhili/p/PrototypePattern.html ICloneable接口 https://msdn.microsoft.com/en-us/library/system.icloneable(vvs.110).aspx Supports cloning, which creates a new instance of a class with the same value as an existing instance. Rem…

I / O神秘化

由于对高度可扩展的服务器设计的所有炒作以及对nodejs的狂热&#xff0c;我一直想重点研究IO设计模式&#xff0c;直到现在为止都没有足够的时间进行投资。 现在已经做了一些研究&#xff0c;我认为最好记下我遇到的东西&#xff0c;作为对我以及可能遇到这篇文章的任何人的未来…

java三大特性 继承_java基础(二)-----java的三大特性之继承

在《Think in java》中有这样一句话&#xff1a;复用代码是Java众多引人注目的功能之一。但要想成为极具革命性的语言&#xff0c;仅仅能够复制代码并对加以改变是不够的&#xff0c;它还必须能够做更多的事情。在这句话中最引人注目的是“复用代码”,尽可能的复用代码使我们程…

Maven本地仓库配置

本地仓库是远程仓库的一个缓冲和子集&#xff0c;当你构建Maven项目的时候&#xff0c;首先会从本地仓库查找资源&#xff0c;如果没有&#xff0c;那么Maven会从远程仓库下载到你本地仓库。这样在你下次使用的时候就不需要从远程下载了。如果你所需要的jar包版本在本地仓库没有…

配置CDI对话的超时

在开发JSF应用程序时&#xff0c;CDI对话范围是一个很好的功能。 假设您有大型数据表&#xff0c;需要花费很长时间才能加载。 由于高内存消耗&#xff0c;您通常不希望将加载的数据放在会话范围的Bean中。 而且&#xff0c;您不能将加载的数据放入视图范围的Bean中&#xff0c…

记录下log4j的两种配置方式

XML文件配置 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4jhttp://jakarta.apache.org/log4j/><!-- 输出到控制台 --><appender na…

java字符串与数组比较大小_java-如何将存储在数组中的字符串与简单字符串进行比较?...

我想比较数组中字符串形式的学生人数与人数n这是字符串。remarque&#xff1a;班级形成&#xff1a;私有字符串代码&#xff1b;私有字符串名称&#xff1b;private int nbsi 0;私人学生[]标签新学生[200]&#xff1b;班级学生&#xff1a;私有字符串号&#xff1b;私有字符串…

delphi用TAdoStoredProc调用存储过程,兼容sql2005、2008、2014的远程事务问题

delphi7写的程序&#xff0c;在sql2000里没问题&#xff0c;调用sql2008、2014里的存储过程时&#xff0c;如果存储过程里操作了大量数据&#xff0c;很容易会莫名其妙的自己撤销掉&#xff0c;但是程序还识别不到&#xff0c;认为还在正常执行。今天尝试换了个控件&#xff1a…