1. 程式人生 > >反片語(UVa 156)

反片語(UVa 156)

Description:
輸入一些單詞,找出所有滿足如下條件的單詞:該單詞不能通過字母重排,得到輸入文字中的另外一個單詞。在判斷是否滿足條件時,字母不分大小寫,但在輸出時應保留輸入中的大小寫,按字典序進行排列(所有大寫字母在所有小寫字母的前面)
Sample input:
Disk came acMe am

Sample output:
Disk
am
這個是演算法競賽入門經典(第二版)map中的例題,在第113頁,自己剛開始看真的看不懂,所以懂了之後就想記錄的詳細點,或許可以給和我一樣有點迷的人一些參考:
首先題目的意思是相同字母組成的單詞不論字母順序和大小寫都算同一個單詞,輸出的應該是用一些字母組成的單詞只出現過一次的,比如acme和caMe就是一個單詞;
這道題最巧妙的是用動態陣列vertor(words)儲存上所有輸入的單詞,然後進行標準化,標準化寫成了一個函式,例如將單詞caMe標準化後就變成了acem.
用map(cnt)儲存標準化後的單詞,因為map是對映,可以計算單詞個數。

#include<iostream>
#include<cstdio>
#include<cctype>//用到了tolower()函式 
#include<map>//map就是從鍵key到值value的對映 
#include<algorithm>//用到了sort()函式 
#include<string> //string相當於一個字元陣列 
#include<vector>//vector是不定長陣列 
using namespace std;
map<string,int> cnt;//cnt儲存標準化後的單詞 
vector<string> words;//words儲存所有的單詞 
string  repr(const string &s){//看其他解析說const是為了不改變s的初值,但是我覺得先把原單詞存到words中了,應該可以不用,我試著去掉後沒問題 
	string ans=s;
	for(int i=0;i<ans.length();i++)
	    ans[i]=tolower(ans[i]);//將單詞中所有字母都變為小寫字母 
	sort(ans.begin(),ans.end() );//將單詞打亂重排,好分析有沒有用相同字母組成的單詞 
	return ans;
}
int main(){
	int n=0;//不知道這句有什麼用,去掉也沒發現什麼問題
	string s;
	while(cin>>s){
		if(s[0]=='#') break;//遇到#表示輸入結束 
		words.push_back(s);//將每個單詞都按照原來的形式存入words中 
		string r=repr(s);//單詞標準化 
		if(!cnt.count(r)) cnt[r]=0;//cnt.count(r)返回map中r的個數;若沒有我們將該單詞加入map,個數設定為0; 
		cnt[r]++;//統計每個單詞的個數(標準化了) 
	}
	vector<string> ans;
	for(int i=0;i<words.size();i++)//words中的每個單詞標準化後去map中查個數 
	    if(cnt[repr(words[i])]==1) ans.push_back(words[i]);//若為1符合題意,沒有相同字母組成的其他單詞,賦給ans 
	sort(ans.begin(),ans.end());//ans中單詞按字典序排序 
	for(int i=0;i<ans.size();i++)
	    cout<<ans[i]<<"\n";
	return 0;
}

啊我真的好囉嗦!看程式碼不太清楚還是自己舉個例子好理解,比如我的輸入是Disk came acMe am #
那麼words陣列中就是這四個元素:Disk came acMe am;
而cnt中的四個元素是:diks acem acem am;(其中cnt[acme]=2,cnt[diks]=1, cnt[am]=1)
ans中的元素就是:Diks am。