程序员面试金典——解题总结: 9.18高难度题 18.5有个内含单词的超大文本文件,给定任意两个单词,找出在这个文件中这两个单词的最短距离

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值