您的位置:首页 > 职场人生

程序员面试金典——解题总结: 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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐