UVa OJ 148 – Anagram checker (迴文構詞檢測)

來源:互聯網
上載者:User

Time limit: 3.000 seconds
限時:3.000秒

 

Problem
問題

It is often fun to see if rearranging the letters of a name gives an amusing anagram. For example, the letters of 'WILLIAM SHAKESPEARE' rearrange to form 'SPEAK REALISM AWHILE'.
經常會發現將一些短語中的字母重新排列後又形成了有趣的新短語。比如“WILLIAM SHAKESPEARE”可以重新排列為“SPEAK REALISM AWHILE”。(譯註,前者是“威廉莎士比亞”,後者為“說段現實主義”)

 

Write a program that will read in a dictionary and a list of phrases and determine which words from the dictionary, if any, form anagrams of the given phrases. Your program must find all sets of words in the dictionary which can be formed from the letters in each phrase. Do not include the set consisting of the original words. If no anagram is present, do not write anything, not even a blank line.
寫一個程式,讀入字典和一些短語,並在字典中找出哪些單詞可以通過重新排列和組合構成給定的短語。對於每一個短語,字典中可能存在多個可以重組為該短語的單詞集合,你的程式要把所有這些單詞集合都找出來。但不要包括與短語中已有的單詞全部重合的單詞集合。如果不存在可以重組為給定短語的單詞集合,則無需輸出任何結果,包括空行。

 

Input
輸入

Input will consist of two parts. The first part is the dictionary, the second part is the set of phrases for which you need to find anagrams. Each part of the file will be terminated by a line consisting of a single #. The dictionary will be in alphabetic order and will contain up to 2000 words, one word per line. The entire file will be in upper case, and no dictionary word or phrase will contain more than 20 letters. You cannot assume the language being used is English.
輸入包括兩部分。第一部分是字典,第二部分是一些短語,作為你要尋找的對象。資料的各部分均由獨佔一行的單個#號表示結束。字典將按字母表順序給出,最多包括2000個單詞,每個單詞獨佔一行。輸入的資料全部由大寫字母構成,所有字典單詞和短語都最多包含20個字母。你不要以為輸入的都是英語。

 

Output
輸出

Output will consist of a series of lines. Each line will consist of the original phrase, a space, an equal sign (=), another space, and the list of words that together make up an anagram of the original phrase, separated by exactly one space. These words must appear in alphabetic sequence.
輸出由多行組成,每行包括原始短語,一個空格,一個等號(=),再一個空格,後面是可以重組為該短語的單字清單,兩個單詞間由一個空格隔開。這些單詞都要按照字母順序輸出。

 

Sample input
輸入樣本

ABC
AND
DEF
DXZ
K
KX
LJSRT
LT
PT
PTYYWQ
Y
YWJSRQ
ZD
ZZXY
#
ZZXY ABC DEF
SXZYTWQP KLJ YRTD
ZZXY YWJSRQ PTYYWQ ZZXY
#

 

Sample output
輸出樣本

SXZYTWQP KLJ YRTD = DXZ K LJSRT PTYYWQ
SXZYTWQP KLJ YRTD = DXZ K LT PT Y YWJSRQ
SXZYTWQP KLJ YRTD = KX LJSRT PTYYWQ ZD
SXZYTWQP KLJ YRTD = KX LT PT Y YWJSRQ ZD

 

Analysis
分析

貌似除了遞迴式暴力搜尋外,沒有其它的有效演算法了。這樣的話,對代碼效能的要求就比較高了,為了快速的判斷一個短語是否包含某個單詞,必須找出一種特定的資料結構來表示。

我的方法是給每個單詞附加一個26位元組長度的數組,各個元素分別代表a, b, ..., z這26個字母在單詞中出現的次數,把這個數組稱為字串的特徵(簡稱特徵)。對於短語也同樣處理(忽略中間的空格),如果短語的特徵均大於或等於某個單詞的特徵,即可判定該短語包含該單詞。如果要在短語中刪除或添加某個單詞,只需對特徵進行加減即可。

遞迴的過程是:從字典的第一個單詞開始尋找,找到第一個能被短語包含的單詞,從短語特徵中減掉這個單詞並將該單詞加入結果清單;然後將字典的起點移動到這個單詞之後,開始下一級遞迴調用。如果下一級返回,則重新恢複原短語特徵(再加上前面的單詞)並從結果集中移除該單詞,繼續向字典後面尋找。如果在某一級調用的開始發現短語特徵已經減為0,則說明已經找到可以重組為該短語的單詞集,按要求輸出結果即可(注意排除單詞集與短語中的單字清單完全重合的情況)。

在具體實現中,我將結果集直接產生為字串是為了代碼比較清晰,如果要追求效率應另用鏈表或雙端隊列來儲存,以加快添加和刪除的速度。

 

Solution
分析
#include <algorithm>#include <iostream>#include <sstream>#include <string>#include <vector>using namespace std;struct WORD {string str; char Trait[26];bool operator>=(const WORD &Other) { //比較兩個單詞的特徵,確定是否包含int i = 0;for (; i < 26 && Trait[i] >= Other.Trait[i]; ++i);return (i == 26); //全部特徵都大於或等於時,判定存在內含項目關聯性}WORD& operator-=(const WORD &Other) { //從前一個單詞中減掉後面的單詞for (int i = -1; ++i < 26; Trait[i] -= Other.Trait[i]); //特徵操作return *this;}WORD& operator+=(const WORD &Other) { //在前面一個單詞中加上後面的單詞for (int i = -1; ++i < 26; Trait[i] += Other.Trait[i]); //特徵操作return *this;}};void GenTrait(const char *Str, char *Trait) { //根據字串產生特徵for (fill(&Trait[0], &Trait[26], 0); *Str != 0; ++Trait[*Str++ - 'A']);}typedef vector<WORD>::const_iterator DICTITER;//遞迴搜尋,iBeg和iEnd為搜尋字典段的起點和終點。字典必須有序void SearchAnagram(DICTITER iBeg, DICTITER iEnd, WORD &Phrase) {int nZeroCnt = 0;string &str = Phrase.str;//判斷短語中所有字母是否都以在字典中找到for (; nZeroCnt < 26 && Phrase.Trait[nZeroCnt] == 0; ++nZeroCnt);if (nZeroCnt == 26) { //所有字母都找到,輸出結果istringstream ss(str); //判定找到的單詞集是否和原單詞集相同vector<string> SrcWords, GenWords;//從輸出字串中提取原短語中的單字清單和尋找到的單詞集for (string Word; ss >> Word && Word != "="; SrcWords.push_back(Word));for (string Word; ss >> Word; GenWords.push_back(Word));//對原短語中的單字清單排序。尋找到的單詞集已有序sort(SrcWords.begin(), SrcWords.end());if (SrcWords != GenWords) { //判定是否相同cout << str << endl; //不同時輸出結果}return; //返回上一級調用}//從指定的開始到結束,尋找字典段中是否有單詞被短語包含for (DICTITER i = iBeg; i != iEnd; ++i) {if (Phrase >= *i) { //根據特徵進行尋找Phrase -= *i; //將該單詞的特徵從短語特徵中減掉str.push_back(' '); //結果字串的最後加上該短語str.append(i->str);SearchAnagram(i + 1, iEnd, Phrase); //進入下一級尋找過程str.erase(str.length() - i->str.length() - 1); //恢複結果字串Phrase += *i; //恢複短語特徵}}}//主函數int main(void) {vector<WORD> Dict, Subset;//讀入所有字典,並產生每個單詞的特徵for (WORD Word; cin >> Word.str && Word.str != "#";) {GenTrait(Word.str.c_str(), Word.Trait);Dict.push_back(Word);}//讀入所有的短語,逐個處理for (string Line; getline(cin, Line) && Line != "#"; Subset.clear()) {WORD Phrase = {Line}; //保留原短語字串//刪除指定短語中的空格(準備產生特徵)Line.erase(remove(Line.begin(), Line.end(), ' '), Line.end());if (Line.empty()) {continue;}GenTrait(Line.c_str(), Phrase.Trait); //產生特徵//在字典中篩選中被原短語包含的單詞,用作尋找for (vector<WORD>::iterator i = Dict.begin(); i != Dict.end(); ++i) {if (Phrase >= *i) { //根據特定判斷是否被包含Subset.push_back(*i);}}Phrase.str.append(" ="); //按照格式,在原短語後加上空格和=號SearchAnagram(Subset.begin(), Subset.end(), Phrase); //遞迴尋找過程}return 0;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.