1、LCR 063. 单词替换
题目信息:
https://leetcode.cn/problems/UhWRSj/description/
在英语中,有一个叫做 词根( root) 的概念,它可以跟着其他一些词组成另一个较长的单词——我们称这个词为 继承词( successor) 。
例如,词根an,跟随着单词 other ( 其他) ,可以形成新的单词 another ( 另一个) 。
现在,给定一个由许多词根组成的词典和一个句子,需要将句子中的所有继承词用词根替换掉。如果继承词有许多可以形成它的词根,则用最短的词根替换它。
需要输出替换之后的句子。
示例 1 :
输入:dictionary = [ "cat" , "bat" , "rat" ] , sentence = "the cattle was rattled by the battery"
输出:"the cat was rat by the bat"
示例 2 :
输入:dictionary = [ "a" , "b" , "c" ] , sentence = "aadsfasf absbs bbab cadsfafs"
输出:"a a b c"
示例 3 :
输入:dictionary = [ "a" , "aa" , "aaa" , "aaaa" ] , sentence = "a aa a aaaa aaa aaa aaa aaaaaa bbb baba ababa"
输出:"a a a a a a a a bbb baba a"
示例 4 :
输入:dictionary = [ "catt" , "cat" , "bat" , "rat" ] , sentence = "the cattle was rattled by the battery"
输出:"the cat was rat by the bat"
示例 5 :
输入:dictionary = [ "ac" , "ab" ] , sentence = "it is abnormal that this solution is accepted"
输出:"it is ab that this solution is ac"
解题思路:
1、审题: 输入一组字符串数组,和单独的一句话(也是字符串),现在要求在一字符串数组中的子树作为词根,当句子中的单个字符串中存在前缀和词根相同的情况,则该字符串使用词根进行替换,并返回替换后的字符串 2、解题: 涉及到字符串前缀搜索,使用前缀树进行解决 先根据字符串词根数组,构建前缀树,TrieNode为前缀树节点 再遍历句子,根据空格将句子拆分为单个的字符串数组并进行遍历,遍历过程中获取到的字符串,在前缀树中进行搜索,判断是否存在前缀词根
最后将替换后的字符串数组重新组合成字符串,使用空格间隔
代码实现:
string findPrex ( TrieNode & root, string & word)
{
std:: string res = "" ;
TrieNode * node = & root;
for ( auto ch : word)
{
int index = ch - 'a' ;
if ( node-> isWord)
{
return res;
}
if ( node-> children[ index] == nullptr )
{
break ;
}
else
{
node = node-> children[ index] ;
res += ch;
}
}
return node-> isWord ? res : "" ;
}
TrieNode * buildTrie ( vector< string> & dictionary)
{
TrieNode * root = new TrieNode ( ) ;
TrieNode * node = nullptr ;
for ( auto & word : dictionary)
{
node = root;
for ( auto & ch : word)
{
int index = ch - 'a' ;
if ( node-> children[ index] == nullptr )
{
node-> children[ index] = new TrieNode ( ) ;
}
node = node-> children[ index] ;
}
node-> isWord = true ;
}
return root;
}
std:: vector< std:: string> splitBySpace ( const std:: string & sentence)
{
std:: vector< std:: string> words;
std:: istringstream iss ( sentence) ;
std:: string word;
while ( iss >> word)
{
words. push_back ( word) ;
}
return words;
}
std:: string joinWords ( const std:: vector< std:: string> & words)
{
std:: ostringstream oss;
for ( size_t i = 0 ; i < words. size ( ) ; ++ i)
{
if ( i > 0 )
{
oss << " " ;
}
oss << words[ i] ;
}
return oss. str ( ) ;
}
string replaceWords ( vector< string> & dictionary, string sentence)
{
TrieNode * root = buildTrie ( dictionary) ;
std:: vector< std:: string> words = splitBySpace ( sentence) ;
for ( int i = 0 ; i < words. size ( ) ; i++ )
{
std:: string word = words[ i] ;
std:: string prexStr = findPrex ( * root, word) ;
if ( ! prexStr. empty ( ) )
{
words[ i] = prexStr;
}
}
return joinWords ( words) ;
}
2、LCR 064. 实现一个魔法字典
题目信息:
https://leetcode.cn/problems/US1pGT/description/
设计一个使用单词列表进行初始化的数据结构,单词列表中的单词 互不相同 。
如果给出一个单词,请判定能否只将这个单词中一个字母换成另一个字母,使得所形成的新单词存在于已构建的神奇字典中。
实现 MagicDictionary 类:
MagicDictionary ( ) 初始化对象
void buildDict ( String[ ] dictionary) 使用字符串数组 dictionary 设定该数据结构,dictionary 中的字符串互不相同
bool search ( String searchWord) 给定一个字符串 searchWord ,判定能否只将字符串中 一个 字母换成另一个字母,
使得所形成的新字符串能够与字典中的任一字符串匹配。如果可以,返回 true ;否则,返回 false 。
示例:
输入
inputs = [ "MagicDictionary" , "buildDict" , "search" , "search" , "search" , "search" ]
inputs = [ [ ] , [ [ "hello" , "leetcode" ] ] , [ "hello" ] , [ "hhllo" ] , [ "hell" ] , [ "leetcoded" ] ]
输出
[ null, null, false , true , false , false ]
解释
MagicDictionary magicDictionary = new MagicDictionary ( ) ;
magicDictionary. buildDict ( [ "hello" , "leetcode" ] ) ;
magicDictionary. search ( "hello" ) ;
magicDictionary. search ( "hhllo" ) ;
magicDictionary. search ( "hell" ) ;
magicDictionary. search ( "leetcoded" ) ;
解题思路1:
1、审题: 构建一个类,首先输入一个字符串数组作为基础数据,然后调用search查询方法,根据输入的查询参数, 判断是否存在值修改参数字符串中的一个字母就可以匹配到基础数据中的一个字符串 2、解题: 解法一:遍历解法 使用数组保存字符串基础数据,在查询方法search中,使用两层for循环, 第一层for循环为从基础数据数组中,遍历到一个字符串,然后与查询字符串进行内部字符的比较 内层for循环则为字符的比较,使用变量diff标记当前两个字符串存在不同的字符个数,如果超过1个以上则直接返回false
如果遍历到结束,并且diff只有1个不相同的字符,则命中选择返回true
代码实现1:
class MagicDictionary
{
public :
MagicDictionary ( )
{
}
void buildDict ( vector< string> dictionary)
{
m_words = dictionary;
}
bool search ( string searchWord)
{
int diff;
for ( auto & word : m_words)
{
diff = 0 ;
if ( word. length ( ) != searchWord. length ( ) )
{
continue ;
}
for ( int i = 0 ; i < word. length ( ) ; i++ )
{
if ( word[ i] != searchWord[ i] )
{
diff++ ;
if ( diff > 1 )
{
break ;
}
}
}
if ( diff == 1 )
{
return true ;
}
}
return false ;
}
std:: vector< std:: string> m_words;
} ;
解题思路2:
解法二:递归实现 使用前缀树进行构建 然后在查询方法时,使用dfs进行深度优先遍历,遍历过程中分情况:
1、遍历到单词结束,节点也结束,并且中间有变更一次,则命中了 2、遍历到单词和节点值不一样,则变更一次,每个字符有26个字母选择项,要寻找到有节点的下一个节点值 3、直到遍历到空节点或者字符串和节点为单词结尾的位置
代码实现2:
class MagicDictionary
{
public :
MagicDictionary ( ) : root ( new TrieNode ( ) )
{
}
~ MagicDictionary ( )
{
delete root;
root = nullptr ;
}
void buildDict ( vector< string> dictionary)
{
TrieNode * node = nullptr ;
for ( auto word : dictionary)
{
node = root;
for ( int i = 0 ; i < word. length ( ) ; i++ )
{
int index = word[ i] - 'a' ;
if ( node-> children[ index] == nullptr )
{
node-> children[ index] = new TrieNode ( ) ;
}
node = node-> children[ index] ;
}
node-> isWord = true ;
}
}
bool dfs ( TrieNode * node, const string & searchWord, int pos, bool isChange)
{
if ( node == nullptr || pos > searchWord. length ( ) )
{
return false ;
}
if ( pos == searchWord. length ( ) && node-> isWord && isChange)
{
return true ;
}
for ( int i = 0 ; i < 26 ; i++ )
{
bool newChange = false ;
if ( searchWord[ pos] == ( i + 'a' ) )
{
newChange = isChange;
}
else
{
if ( isChange)
{
continue ;
}
else
{
newChange = true ;
}
}
if ( dfs ( node-> children[ i] , searchWord, pos + 1 , newChange) )
{
return true ;
}
}
return false ;
}
bool search ( string searchWord)
{
return dfs ( root, searchWord, 0 , false ) ;
}
TrieNode * root;
} ;
3、总结
前缀树构建,每个节点包含子节点个数为26,根据字符在字母表中的位置,查找对应的子节点在数组中的位置 前缀树查找,也是同样的思路,根据字符位置查找对应结点位置,并不断赋值,直到最后结点的单词标记位为true。