程序员面试金典——解题总结: 9.18高难度题 18.8给定一个字符串s和一个包含较短字符串的数组T,设计一个方法,根据T中的每一个较短字符串,对s进行搜索
2017-01-20 01:44
826 查看
#include <iostream> #include <stdio.h> #include <map> #include <vector> #include <string> using namespace std; /* 问题:给定一个字符串s和一个包含较短字符串的数组T,设计一个方法,根据T中的每一个较短字符串,对s进行搜索 分析:没明白什么意思,什么叫从数组中取出每个字符串,然后再另一个字符串中搜索。 首先实现后缀树。 后缀树含义:树中的每个顺着某个结点往下的结点路径所组成的字符串是原始字符串的后缀。 作用:进行后缀和子串匹配。 查询后缀树返回的结果:子子串在原始字符串中的起始位置,这个位置可能有多个。 实现原理: 生成后缀树: 1设待查找字符串str,设其长度为n,分别遍历以第1个字符为起始字符的字符串,以第2个字符为起始的字符串,...,第n个字符为起始的字符串, 得到n个后缀字符串 2对每个后缀字符串,将其首字符对应在字符串中位置存入集合中,并查找结点中是否有对应以当前后缀字符串第一个字符对应的子结点, 如果有第一个字符对应的子节点(没有该子节点就新建一个),继续向下递归以该子节点来生成除首字符外的剩余字符串的后缀结点; 查找某个字符串在后缀树中的起始位置: 查找字符串中首个字符,寻找到该字符在后缀树中的结点,利用该结点查找除首字符剩余字符串; 如果在查找过程中某个字符没有对应结点,说明该子串不存在,直接返回; 否则,找到该字符串最后一个字符对应的结点,返回该结点中存储的该子串的起始位置 输入: 4(查找字符串的个数n) mississippi(被查找的字符串) is sip hi sis(n个查找的字符串) 输出: 1 4(子串在字符串中起始位置,有多个就全部输出,没有就输出no) 6 no 3 关键: 1 后缀树含义:树中的每个顺着某个结点往下的结点路径所组成的字符串是原始字符串的后缀。 作用:进行后缀和子串匹配。 查询后缀树返回的结果:子子串在原始字符串中的起始位置,这个位置可能有多个。 2实现原理: 生成后缀树: 1设待查找字符串str,设其长度为n,分别遍历以第1个字符为起始字符的字符串,以第2个字符为起始的字符串,...,第n个字符为起始的字符串, 得到n个后缀字符串 2对每个后缀字符串,将其首字符对应在字符串中位置存入集合中,并查找结点中是否有对应以当前后缀字符串第一个字符对应的子结点, 如果有第一个字符对应的子节点(没有该子节点就新建一个),继续向下递归以该子节点来生成除首字符外的剩余字符串的后缀结点; 查找某个字符串在后缀树中的起始位置: 查找字符串中首个字符,寻找到该字符在后缀树中的结点,利用该结点查找除首字符剩余字符串; 如果在查找过程中某个字符没有对应结点,说明该子串不存在,直接返回; 否则,找到该字符串最后一个字符对应的结点,返回该结点中存储的该子串的起始位置 */ //后缀树结点:包含:结点对应的字符,该字符对应的子串在原始字符串中起始位置的集合,字符到结点的孩子结点映射 。 能够执行插入和返回子串下标,销毁的操作。 class SuffixTreeNode { public: SuffixTreeNode(){} ~SuffixTreeNode(){} void insertString(string& str , int index) { _indexs.push_back(index); if(str.empty() || index < 0) { return ; } char value = str[0]; map<char , SuffixTreeNode*>::iterator it = _childs.find(value); SuffixTreeNode* child = NULL; if(it != _childs.end()) { child = it->second; } //结点不存在,创建 else { child = new SuffixTreeNode(); _childs[value] = child; } //递归处理剩余字符串 string sRemainder = str.substr(1);//第二个参数是长度:默认是从当前位置到结尾的长度,所以这里可以忽略 child->insertString(sRemainder , index); // 子字符串和首字符的在原始字符串的起始位置相同 } //返回str在原始字符串中的位置集合 vector<int> getIndexs(string& str) { //如果已经到达后缀树的叶子结点,说明查找到了该子串,返回叶子结点对应的子串在在原始字符串的位置集合(因为子串可能多次出现) if(str.empty()) { return _indexs; } char value = str.at(0); map<char , SuffixTreeNode*>::iterator it = _childs.find(value); if(it != _childs.end()) { //获取剩余字符串,对剩余字符串递归处理 SuffixTreeNode* child = it->second; string remainder = str.substr(1); return child->getIndexs(remainder); } //如果当前字符在孩子结点中找不到,说明该子串无法找到,直接返回结果 else { vector<int> results; return results; } } public: char _value; map<char , SuffixTreeNode*> _childs; vector<int> _indexs; }; //后缀树包含一个根节点,能够返回查询子串的位置 class SuffixTree { public: SuffixTree(string str) { //如果是空字符串,就直接返回,不做任何处理 if(str.empty()) { cout << "String is empty." << endl; return ; } //创建根节点,并对字符串的所有后缀子串进行处理 _root = new SuffixTreeNode(); int length = str.length(); string suffixStr; for(int i = 0 ; i < length ; i++) { suffixStr = str.substr(i); _root->insertString(suffixStr , i); } } ~SuffixTree() { deleteNode(_root); } vector<int> getIndexs(string str) { return _root->getIndexs(str); } private: //删除结点,析构,递归删除 void deleteNode(SuffixTreeNode* root) { map<char , SuffixTreeNode*>::iterator it; for(it = (root->_childs.begin()) ; it != (root->_childs.end()) ; it++) { SuffixTreeNode* child = it->second; if(NULL != child) { deleteNode(child); } } delete root; root = NULL; } public: SuffixTreeNode* _root; }; void process() { int strNum; string searchStr; vector<string> strings; string value; while(cin >> strNum >> searchStr) { strings.clear(); for(int i = 0 ; i < strNum ; i++) { cin >> value; strings.push_back(value); } SuffixTree suffixTree(searchStr); for(int i = 0 ; i < strNum ; i++) { value = strings.at(i); vector<int> indexs = suffixTree.getIndexs(value); if(!indexs.empty()) { int size = indexs.size(); for(int j = 0 ; j < size ; j++ ) { cout << indexs.at(j) << " "; } cout << endl; } else { cout << "no" << endl; } } } } int main(int argc , char* argv[]) { process(); getchar(); return 0; }
相关文章推荐
- 程序员面试金典——解题总结: 9.18高难度题 18.13给定一份几百万个单词的清单,设计一个算法,创建由字母组成的最大矩形
- 程序员面试金典——解题总结: 9.18高难度题 18.11给定一个方阵,其中每个单元(像素)非黑即白。设计一个算法,找出四条边都是黑色像素的最大子方阵。
- 程序员面试金典——解题总结: 9.18高难度题 18.6设计一个算法,给定10亿数字,找出最小的100万个数字。假定计算机内存足以容纳全部10亿个数字。
- 程序员面试金典——解题总结: 9.18高难度题 18.3编写一个方法,从大小为n的数组中随机选出m个整数。要求每个元素被选中的概率相同。
- 程序员面试金典——解题总结: 9.18高难度题 18.10给定两个字典里的单词,长度相等。编写一个方法,将一个单词变换成另一个单词,一次只改动一个字母。
- 程序员面试金典——解题总结: 9.18高难度题 18.9随机生成一些数字并传入某个方法。编写一个程序,每当收到新数字时,找出并记录中位数。
- 程序员面试金典——解题总结: 9.18高难度题 18.4编写一个方法,数出0到n(含)中数字2出现了几次
- 程序员面试金典——解题总结: 9.18高难度题 18.12给定一个正整数和负整数组成的N*M矩阵,编写代码找出元素总和最大的子矩阵。
- 程序员面试金典——解题总结: 9.18高难度题 18.8---拓展: 实现一个Trie树
- 程序员面试金典——解题总结: 9.18高难度题 18.7给定一组单词,编写一个程序,找出其中的最长单词,且该单词由这组单词中的其他单词组合而成。
- 程序员面试金典——解题总结: 9.18高难度题 18.2编写一个方法,洗一副牌。要求做到完美洗牌,换言之,这副牌52!种排列组合出现的概率相同
- 程序员面试金典——解题总结: 9.17中等难题 17.11给定rand5(),实现一个方法rand7()。也就是,给定一个产生0到4(含)随机数的方法,编写一个产生0到6(含)随机数的方法
- 程序员面试金典——解题总结: 9.18高难度题 18.5有个内含单词的超大文本文件,给定任意两个单词,找出在这个文件中这两个单词的最短距离
- 程序员面试金典——解题总结: 9.17中等难题 17.8给定一个整数数组(有正数有负数),找出总和最大的连续数列,并返回总和
- 程序员面试金典——解题总结: 9.17中等难题 17.12设计一个算法,找出数组中两数之和为指定值的所有整数对。
- 程序员面试金典——解题总结: 9.17中等难题 17.9设计一个方法,找出任意指定单词在一本书中的出现频率
- 程序员面试金典——解题总结: 9.17中等难题 17.6给定一个整数数组,编写一个函数,找出索引m和n,只要将m和n之间的元素排好序,整个数组就是有序的。注意:n - m越小越好,也就是说,找出
- 程序员面试金典: 9.11 排序与查找 11.5有个排序后的字符串数组,其中散布这一些空字符串,编写一个方法,找出给定字符串的位置
- 给定一个整数,把整数中包含的每一个数字放到一个数组中(不要把数字转化为字符串处理)
- 程序员面试金典——解题总结: 9.18高难度题 18.1编写一个函数,将两个数字相加。不得使用+或其他算术运算符。