#include <iostream>
#include <stdio.h>
#include <string>
#include <vector>
#include <queue>
#include <hash_map>
#include <fstream>
#include <map>
#include <set>
#include <sstream>
using namespace std;
/*
问题:给定两个字典里的单词,长度相等。编写一个方法,将一个单词变换成另一个单词,一次只改动一个字母。
在变换过程中,每一步得到的新单词都必须是字典里里存在的。
分析:设原始字符串src[1..n],长度为n,目标字符串为dst
首先可以找对应不同的字母,尝试直接变换,比如找到小标为i的字符不同,尝试
将src[i]直接变换为dst[i],如果变换后的源字符串src不在字典中,说明不能这样操作,就需要寻找
中间的某个替代字符。
书上解法:我卡在了寻找中间替代字符。书上结果用了回溯,果然想法是一样的。
用广度优先,设定原始字符串压入队列,设定每次从队列中弹出字符串和目标字符串比较,
如果相同,则寻找到上一个可以经过修改一个字符等于目标字符串的字符串,再根据该字符串重复上述操作,得到结果;
如果不相同,则寻找变成当前字符串只需改动一个字符的字符串集合,遍历其中每个字符串,如果
没有访问过就加入队列,并设置该字符串可以变成当前字符串。
输入:
word(原始字符串) well(目标字符串)
bed bag
bed bad
输出:
word wold weld well(从原始字符串每次变动一个字符变成目标字符串的集合)
bed bad bag
bed bad
关键:
1我卡在了寻找中间替代字符。书上结果用了回溯,果然想法是一样的。
用广度优先,设定原始字符串压入队列,设定每次从队列中弹出字符串和目标字符串比较,
如果相同,则寻找到上一个可以经过修改一个字符等于目标字符串的字符串,再根据该字符串重复上述操作,得到结果;
如果不相同,则寻找变成当前字符串只需改动一个字符的字符串集合,遍历其中每个字符串,如果
没有访问过就加入队列,并设置该字符串可以变成当前字符串。
2 一定要生成当前字符串经过一次字符变换后的字符串集合;先建立字符串指向回溯关系,再判断是否找到
while(!queueStr.empty())
{
string value = queueStr.front();
queueStr.pop();
//寻找经过一次字符变换就可以生成的单词v
newWords = getOneEditWords(value , words);
for(set<string>::iterator it = newWords.begin() ; it != newWords.end() ; it++)
{
string newWord = *it;
//应该先判断,如果新单词在字典中存在,就建立映射,否则映射还没建立,就找到结果,回溯的时候,就无法回溯
if( words.find(newWord) != words.end() )
{
//如果新单词没有访问过,就压入队列
if( visitedMap.find(newWord) == visitedMap.end() )
{
queueStr.push(newWord);
visitedMap[newWord] = true;
resultMap[newWord] = value;//设置指向,这个要放到前面去做
}
}
//说明找到了,直接寻找结果
if(newWord == dst)
{
resultVector.push_back(newWord);
map<string , string>::iterator itFind = resultMap.find(newWord);
while(itFind != resultMap.end())
{
string previousStr = itFind->second;
resultVector.push_back(previousStr);
itFind = resultMap.find(previousStr);
}
return resultVector;
}
}
}
*/
const int MAXSIZE = 1024;
//读取字典中每一行,截取出英文单词,存放到哈希表中
void readDictionary(string& fileName , hash_map<string , int>& words)
{
ifstream infile(fileName , ios::in);
if(!infile)
{
cout << fileName << " can not open" << endl;
return;
}
char line[MAXSIZE];
int index = -1;
string word;
while(!infile.eof())
{
infile.getline(line , MAXSIZE);
//找到"|"截取出前面的部分作为单词
string sLine(line);
index = sLine.find("|");
word = sLine.substr(0 , index);
//将单词转换为小写
transform(word.begin() , word.end() , word.begin() , tolower);
if(words.find(word) == words.end())
{
words.insert(pair<string , int>(word , 1));
}
}
infile.close();
}
set<string> getOneEditWords(string& word , hash_map<string , int>& words)
{
set<string> resultSet;
if(word.empty())
{
return resultSet;
}
int length = word.length();
for(int i = 0 ; i < length ; i++)
{
char value = word.at(i);
//注意这里要留取待截取字符串前面的位置i,不是i+1
string frontStr = word.substr(0 , i);
string backStr = word.substr(i+1);
for(char ch = 'a' ; ch <= 'z' ; ch++)
{
if(ch != value)
{
//尝试组成新的单词
stringstream stream;
stream << frontStr << ch << backStr;
string newWord = stream.str();
//如果该单词存在于字典中,则加入结果集
if(words.find(newWord) != words.end())
{
resultSet.insert(newWord);
}
}
}
}
return resultSet;
}
vector<string> getTransformStrings(string& src, string& dst ,hash_map<string ,int>& words )
{
vector<string> resultVector;
if(src.empty() || dst.empty() || words.empty())
{
return resultVector;
}
//注意,一定要将字符串全部改成小写(因为我的字典中单词是小写的)
transform(src.begin() , src.end() , src.begin() , tolower);
transform(dst.begin() , dst.end() , dst.begin() , tolower);
queue<string> queueStr;
hash_map<string , bool> visitedMap;//是否访问的标记,true表示已经访问过
visitedMap[src] = true;
queueStr.push(src);
map<string ,string> resultMap;
set<string> newWords;
while(!queueStr.empty())
{
string value = queueStr.front();
queueStr.pop();
//寻找经过一次字符变换就可以生成的单词v
newWords = getOneEditWords(value , words);
for(set<string>::iterator it = newWords.begin() ; it != newWords.end() ; it++)
{
string newWord = *it;
//应该先判断,如果新单词在字典中存在,就建立映射,否则映射还没建立,就找到结果,回溯的时候,就无法回溯
if( words.find(newWord) != words.end() )
{
//如果新单词没有访问过,就压入队列
if( visitedMap.find(newWord) == visitedMap.end() )
{
queueStr.push(newWord);
visitedMap[newWord] = true;
resultMap[newWord] = value;//设置指向,这个要放到前面去做
}
}
//说明找到了,直接寻找结果
if(newWord == dst)
{
resultVector.push_back(newWord);
map<string , string>::iterator itFind = resultMap.find(newWord);
while(itFind != resultMap.end())
{
string previousStr = itFind->second;
resultVector.push_back(previousStr);
itFind = resultMap.find(previousStr);
}
return resultVector;
}
}
}
return resultVector;
}
void print(vector<string>& resultVector)
{
if(resultVector.empty())
{
cout << "No result" << endl;
return;
}
int size = resultVector.size();
for(int i = size - 1 ; i >= 0 ; i--)
{
cout << resultVector.at(i) << " ";
}
cout << endl;
}
void process()
{
hash_map<string ,int> words;
string fileName("./dictionary.txt");
readDictionary(fileName , words);
string src;
string dst;
vector<string> resultVector;
while(cin >> src >> dst)
{
resultVector = getTransformStrings(src, dst , words);
print(resultVector);
}
}
int main(int argc , char* argv[])
{
process();
getchar();
return 0;
}
程序员面试金典——解题总结: 9.18高难度题 18.10给定两个字典里的单词,长度相等。编写一个方法,将一个单词变换成另一个单词,一次只改动一个字母。
最新推荐文章于 2023-01-27 19:56:32 发布