#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;
/*
问题:有个内含单词的超大文本文件,给定任意两个单词,找出在这个文件中这两个单词的最短距离(也即相隔几个单词)。有办法在O(1)时间里完成搜索操作吗?
解法的空间复杂度如何?
分析:这个是编程之美的最短摘要算法。简单的例子:
假设寻找的是: "m" "a",
假设文本为: c d a a b e m m r a e g g m
关键就是要确保搜索中一直能够移动下标,
设字符串数组为A,遍历当前字符串数组,如果寻找到两个单词中其中一个,记录其下标为i,如果后续继续找到某个单词记录其下标j
如果A[i] = A[j],说明两个单词相同,则令j=-1,
否则,说明两个单词不相同,记录单词距离d=|j-i|,如果d < 最小单词距离minDis,更新minDis=d,minIndex1=i,minIndex2=j
不断移动两个单词的下标,记录最短距离和最短距离单词所在位置
输入:
m a(n个待搜索的单词)
14(文本中单词个数)
c d a a b e m m r a e g g m
输出:
2(最短距离)
m r a(最短摘要,从搜索的单词1开始到搜索的单词2结尾)
关键:
1 书上解法:最简单的是遍历字符串列表,找到每个单词出现的下标,将属于同一个单词的下标集合以数组形式存放,然后将两个数组排序,
选取出自不同数组的连续差值最小的作为结果即可
比如: 对于单词数组 : c d a a b e m m r a e g g m
单词m 出现的下标集合:{6,7,13}
单词a :{2,3,9}
标记数组:{2a,3a,6b,7b,9a,13b}
2 设字符串数组为A,遍历当前字符串数组,如果寻找到两个单词中其中一个,记录其下标为i,如果后续继续找到某个单词记录其下标j
如果A[i] = A[j],说明两个单词相同,则令j=-1,
否则,说明两个单词不相同,记录单词距离d=|j-i|,如果d < 最小单词距离minDis,更新minDis=d,minIndex1=i,minIndex2=j
不断移动两个单词的下标,记录最短距离和最短距离单词所在位
3 此题与之前的编程之美:最短摘要生成不同的是,最短摘要生成是通过:首先设置起始,截止指针begIndex,endIndex,遍历字符串找到一组下标begIndex,endIndex,
使得能够包含所有待查找字符串;
没有找到最短摘要,就endIndex++;
找到最短摘要,就begIndex++; 即让某个被查找的元素不再包含到最短摘要中,这样来促使endIndex继续移动发生变化
*/
typedef struct Result
{
Result(){}
Result(int index1 , int index2 , int mDis , string& sResult):
_index1(index1),_index2(index2),_minDis(mDis),_searchResult(sResult){}
int _index1;//单词1的起始位置
int _index2;//单词2的起始位置
int _minDis;
string _searchResult;
}Result;
enum ArrayFlag{ARRAY1 , ARRAY2};
typedef struct Value
{
Value(int value, ArrayFlag flag):_value(value),_arrayFlag(flag){}
int _value;
ArrayFlag _arrayFlag;
bool operator < (const Value& other) const
{
return _value < other._value;
}
}Value;
Result search2(vector<string>& words , string& word1 , string& word2)
{
Result invalidResult(-1, -1 , -1, string(""));
if(words.empty() || word1.empty() || word2.empty() || word1 == word2)
{
return invalidResult;
}
vector<Value> indexs;
int size = words.size();
for(int i = 0 ; i < size ; i++)
{
//每当找到一个单词时,就进行比较
if( words.at(i) == word1 )
{
indexs.push_back(Value( i , ARRAY1));
}
else if( words.at(i) == word2 )
{
indexs.push_back(Value( i , ARRAY2));
}
}
sort(indexs.begin() , indexs.end());
//排好序遍历相邻两个来自不同数组的元素
int length = indexs.size();
int minDis = INT_MAX;
Result result;
for(int i = 1 ; i < length ; i++)
{
//两个不同的数组
if(indexs.at(i-1)._arrayFlag != indexs.at(i)._arrayFlag)
{
int dis = abs( indexs.at(i-1)._value - indexs.at(i)._value);
if(dis < minDis)
{
minDis = dis;
result._minDis = minDis;
result._index1 = indexs.at(i-1)._value;
result._index2 = indexs.at(i)._value;
}
}
}
//如果单词1在前面
int beginIndex = result._index1 < result._index2 ? result._index1 : result._index2;
int endIndex = result._index1 > result._index2 ? result._index1 : result._index2;
stringstream stream;
for(int i = beginIndex ; i <= endIndex ; i++)
{
stream << words.at(i) << " ";
}
result._searchResult = stream.str();
return result;
}
Result search(vector<string>& words , string& word1 , string& word2)
{
Result invalidResult(-1, -1 , -1, string(""));
if(words.empty() || word1.empty() || word2.empty() || word1 == word2)
{
return invalidResult;
}
int pos1 = -1 , pos2 = -1;
int size = words.size();
int minDis = INT_MAX;
Result result;
bool isFind = false;
for(int i = 0 ; i < size ; i++)
{
//每当找到一个单词时,就进行比较
if( words.at(i) == word1 )
{
pos1 = i;
isFind = true;
}
else if( words.at(i) == word2 )
{
pos2 = i;
isFind = true;
}
//如果找到了某个单词,开始进行比较
if(!isFind)
{
continue;
}
//如果另一个单词已经确定位置了
if(pos2 >= 0 && pos1 >= 0)
{
int dis = abs(pos1 - pos2);
if(dis < minDis)
{
minDis = dis;
result._minDis = minDis;
result._index1 = pos1;
result._index2 = pos2;
}
//是否更新当前pos为新找到的pos:如果发现产生了最短距离就更新;否则说明找到的pos位置很远,如果更新,会导致后续的搜索结果都比较差;
//一定要更新新找到的位置,因为最好的结果已经保存了,即使更新后会使得后续结果变差,但不影响
}
isFind = false;
}
//如果单词1在前面
int beginIndex = result._index1 < result._index2 ? result._index1 : result._index2;
int endIndex = result._index1 > result._index2 ? result._index1 : result._index2;
stringstream stream;
for(int i = beginIndex ; i <= endIndex ; i++)
{
stream << words.at(i) << " ";
}
result._searchResult = stream.str();
return result;
}
void process()
{
int searchWordNum;
vector<string> words;
//这里假设读取的文章是一个大的字符串
string text;
string searchWord;
string word1 , word2;
while(cin >> word1 >> word2)
{
words.clear();
cin >> searchWordNum;
for(int i = 0 ; i < searchWordNum ; i++)
{
cin >> searchWord;
words.push_back(searchWord);
}
//Result result = search(words , word1, word2);
Result result = search2(words , word1, word2);
cout << result._minDis << endl << result._searchResult << endl;
}
}
int main(int argc , char* argv[])
{
process();
getchar();
return 0;
}