选择一个有效的数据结构来寻找韵律
Choosing an efficient data structure to find rhymes
我一直在开发一个程序,该程序可以读取整个词典,并利用CMU的WordNet将每个单词拆分为其发音。
目标是利用字典找到给定单词的最佳押韵和头韵,给定我们需要找到的单词中的音节数量及其词性。
我决定使用std::map<std::string, vector<Sound> >
和std::multimap<int, std::string>
,其中映射将字典中的每个单词映射到其在向量中的发音,并且多映射是从一个函数返回的,该函数查找与给定单词押韵的所有单词。
int
是对应单词的音节数,string
保存单词。
我一直在研究效率,但似乎没有比O(n)
更高效的了。我找到所有与给定单词押韵的单词的方法是
vector<string> *rhymingWords = new vector<string>;
for (iterator it : map<std::string, vector<Sound> >) {
if(rhymingSyllables(word, it.first) >= 1 && it.first != word) {
rhymingWords->push_back(it.first);
}
}
return rhymingWords;
当我找到一个单词的最佳押韵(一个与给定单词押韵最多的单词)时,我会进行
vector<string> rhymes = *getAllRhymes(rhymesWith);
int x = 0;
for (string s : rhymes) {
if (countSyllables(s) == numberOfSyllables) {
int a = rhymingSyllables(s, rhymesWith);
if (a > x) {
maxRhymes = thisRhyme;
bestRhyme = s;
}
}
}
return bestRhyme;
缺点是就字典中的单词数量而言O(n)
访问时间。我在想把它降到O(log n)
的想法,但似乎每次都会走到死胡同。我曾考虑过使用树状结构,但无法确定具体细节。
有什么建议吗?谢谢
rhymingSyllables
功能是这样实现的:
int syllableCount = 0;
if((soundMap.count(word1) == 0) || (soundMap.count(word2) == 0)) {
return 0;
}
vector<Sound> &firstSounds = soundMap.at(word1), &secondSounds = soundMap.at(word2);
for(int i = firstSounds.size() - 1, j = secondSounds.size() - 1; i >= 0 && j >= 0; --i, --j){
if(firstSounds[i] != secondSounds[j]) return syllableCount;
else if(firstSounds[i].isVowel()) ++syllableCount;
}
return syllableCount;
p.S。vector<Sound>
是单词的发音,其中Sound
是包含英语词素的每个不同发音的类:即,AA vowel AE vowel AH vowel AO vowel AW vowel AY vowel B stop CH affricate D stop DH fricative EH vowel ER vowel EY vowel F fricative G stop HH aspirate IH vowel IY vowel JH affricate K stop L liquid M nasal N nasal NG nasal OW vowel OY vowel P stop R liquid S fricative SH fricative T stop TH fricative UH vowel UW vowel V fricative W semivowel Y semivowel Z fricative ZH fricative
也许你可以对押韵过程中匹配的词素进行分组,并不是比较词素的向量,而是比较相关组的向量。然后你可以对字典进行一次排序,得到对数搜索时间。
在研究了押韵音节的实现之后,似乎可以将单词转换为声音,然后将任何元音相互匹配,并且只有在其他声音相同的情况下才匹配。因此,应用上面的建议,你可以引入一个额外的辅助音"anyVowel",然后在构建词典的过程中,将每个单词转换为其声音,用"anyVowell"替换所有元音,并将该表示推送到词典中。一旦你完成了对字典的分类。当你想搜索一个单词的押韵时,将其转换为相同的表示形式,并在字典上进行二进制搜索,首先按最后一个音作为关键字,然后按前一个音,依此类推。这将给你m*log(n)最坏情况下的复杂度,其中n是字典大小,m是单词长度,但通常它会终止得更快。
你也可以利用这样一个事实,即为了获得最佳押韵,你只考虑具有特定音节数的单词,并为每个音节数保留一个单独的词典。然后你们计算出单词中的音节数,并在合适的字典中搜索。渐进地说,它不会给你任何增益,但它提供的加速可能对你的应用程序有用。
我一直在考虑这个问题,我可能会提出一种算法的方法。
我可能会先拿字典,把它分成多个桶或批次。其中,每个批次表示每个单词的音节数。要存储到不同存储桶中的向量的遍历应该是线性的,因为您正在遍历一个大的字符串向量。从这里开始,由于第一个桶将包含1个音节的所有单词,所以目前没有什么可做的,所以你可以跳到第二个桶,之后的每个桶都需要提取每个单词并将每个单词的音节分开。因此,如果你有25个桶,你知道前几个和后几个桶不会容纳太多单词,他们的时间不应该很长,应该先完成,然而,中间有3-5或3-6个音节长度的桶将是最大的,所以如果它们的大小超过一定量,你可以在一个单独的线程上运行这些桶中的每一个,并让它们并行运行。现在,一旦你完成了;每个bucket应该返回一个std::vector<std::shared_ptr<Word>>
,其中您的结构可能如下所示:
enum SpeechSound {
SS_AA,
SS_AE,
SS_...
SS_ZH
};
enum SpeechSoundType {
ASPIRATE,
...
VOWEL
};
struct SyllableMorpheme {
SpeechSound sound;
SpeechSoundType type;
};
class Word {
public:
private:
std::string m_strWord;
// These Two Containers Should Match In Size! One String For Each
// Syllable & One Matching Struct From Above Containing Two Enums.
std::vector<std::string> m_vSyllables
std::vector<SyllableMorpheme> m_vMorphemes;
public:
explicit Word( const std::string& word );
std::string getWord() const;
std::string getSyllable( unsigned index ) const;
unsigned getSyllableCount() const;
SyllableMorpheme getMorhpeme( unsigned index ) const;
bool operator==( const ClassObj& other ) const;
bool operator!=( const ClassObj& other ) const;
private:
Word( const Word& c ); // Not Implemented
Word& operator=( const Word& other ) const; // Not Implemented
};
这一次,您将拥有这些类对象的共享指针的新桶或向量。然后,您可以轻松地编写一个函数来遍历每个bucket,甚至多个bucket,因为bucket将具有相同的签名,只有不同数量的音节。回想起每个bucket应该已经按照字母顺序进行了排序,因为我们只根据音节数添加它们,并且从未更改从字典中读取的顺序。
有了这个,你可以在检查匹配音节和词素时很容易地比较两个单词是否相等。这些都包含在CCD_ 13中。所以你也不必担心记忆会被清理掉。
其思想是尽可能多地使用线性搜索、分离和比较;但是,如果你的容器太大,那么创建bucket并在多个线程中并行运行,或者如果它能满足你的需求,可以使用哈希表。
这个类结构的另一种可能性是,如果你想或需要的话,你甚至可以在以后添加更多的内容,比如另一个std::vector用于它的定义,另一个std::vector<string>
用于它的词性{名词、动词等}。你甚至可以添加其他vector<string>
用于同音词、同音词,甚至可以添加vector<string>
用于与它押韵的所有单词的列表。
现在,对于你寻找最佳匹配押韵的特定任务,你可能会发现有些单词最终可能会有一个单词列表,这些单词都被认为是最佳匹配或最适合的!因此,您不想存储或返回单个字符串,而是希望存储或返回一个字符串向量!
案例示例:
To Too Two Blue Blew Hue Hew Knew New,
Bare Bear Care Air Ayre Heir Fair Fare There Their They're
Plain, Plane, Rain, Reign, Main, Mane, Maine
是的,这些都是单音节押韵词,但正如你所看到的,在很多情况下,有多个有效答案,而不仅仅是一个最佳匹配。这确实需要考虑。
- 链表,反向函数,数据结构
- 如何使用set实现无序数据结构?
- 我们可以将数据永久保存为数据结构吗?
- C++中的可变长度数组/数据结构
- 用于存储由空格分隔的字符串的 C++/C 数据结构
- 通过 NIF 从C++返回自定义数据结构
- 编译器上的策略数据结构不起作用
- 尝试构建"lock-free"数据结构C++
- 设计将引用元素移动到开头的数据结构.C++
- 在学习数据结构之前对STL有一个了解是好的吗?
- 如何解析表示树状数据结构的字符串
- 我对数据结构、双向链表有一些问题
- googletest:测试太大的数据结构
- C++中deque数据结构的大O是什么?
- 寻找快速初始化和快速查找的数据结构 (O(1))
- 寻找一种提供随机和"sequential"访问的数据结构
- 选择一个有效的数据结构来寻找韵律
- 寻找适合工作的最佳数据结构
- 寻找特殊的c++数据结构
- 寻找一个良好的空间分区数据结构来快速生成数百万个原子键