国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

C++變位詞問題分析

2020-01-26 15:24:38
字體:
來源:轉載
供稿:網友

在《編程珠璣》一書的第二章提到了一個變位詞問題,變位詞指的是一個單詞可以通過改變其他單詞中字母的順序來得到,也叫做兄弟單詞,如army->mary。由變位詞可以引申出幾個算法問題,包括字符串包含問題,比較兩個字符串是否是變位詞,以及找出字典中變位詞集合的問題。

一、字符串包含問題

(1) 問題描述:存在字符串1和字符串2,假設字符串2相對較短,如何快速地判定字符串2中的字符都存在于字符串1里(假定字符串只包含字母)?

(2) 舉例:字符串1為ABCDEFGHIJK,字符串2為ABCDE,則字符串1包含字符串2,因為字符串2中包含的字母在字符串1中也都有。

(3) 解決方案:

思路一

最直接的思路就是針對字符串2中的每個字符,輪詢字符串1進行比較,看是否在字符串1里面。很明顯這種的時間效率為O(n*m)。

/*************************************************************************   > File Name: test.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<string> using namespace std;  void Compare(string long_str, string short_str) {   int i,j;   for(i=0; i<short_str.size(); ++i)   {     for(j=0; j<long_str.size(); ++j)     {       if(long_str[j] == short_str[i])       {         break;       }     }     if(j == long_str.size())     {       cout << "false" << endl;       return;     }   }   cout << "true" << endl;   return; }  int main() {   string l = "ABCDEFGHIJK";   string s = "ABCDEF";   Compare(l, s);   return 0; }

思路二

這里由于假定了字符串只包含字母,所以我們可以用一個額外的數組flag[26]作為26個字符標識位,先遍歷長字符串將對應的標識位置1,再遍歷短字符串,如果對應的標示位都是1,則包含;否則不包含。這種方法的時間復雜度為O(n+m),為了提高空間效率,這里不使用數組而使用26個bit位來作為標示位(bitset容器)。

/*************************************************************************   > File Name: test1.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<bitset> #include<string> using namespace std;  bool Compare(string long_str, string short_str) {   bitset<26> flag;   for(int i=0; i<long_str.size(); ++i)   {     // flag.set(n)置第n位為1     flag.set(long_str[i]-'A');   }   for(int i=0; i<short_str.size(); ++i)   {     // flag.test(n)判斷第n位是否為1     if(!flag.test(short_str[i]-'A'))       return false;   }   return true; }  int main() {   string l = "ABCDEFGHIJK";   string s = "ABCDEZ";   if(Compare(l, s))     cout << "true" << endl;   else     cout << "false" << endl;   return 0; }

這種方法還可以進行優化,例如如果長字串的前綴就為短字串,那么我們可以不需要n+m次,而只需要2m次。具體實現請自己思考。

思路三

給每個字母分配一個素數,從2開始到3,5,7...遍歷長字串,求得每個字符對應素數的乘積。然后遍歷短字串,判斷該乘積能否被短字符串中的字符對應的素數整除,如果除的結果存在余數,則說明有不匹配的字母;如果整個過程都沒有余數,則說明短字串中的字母在長字串里都有。這種方法的時間復雜度也是O(n+m),需要26個額外空間存素數,但是這種方法有一個缺點就是需要跟大整數打交道,因為乘積可能非常大。(這里我們使用<cstdint>頭文件中定義的int64_t和uint64_t)

/*************************************************************************   > File Name: test2.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<string> #include<stdint.h> //#include<cstdint> // C++11 using namespace std;  bool Compare(string long_str, string short_str) {   unsigned int primeNum[26] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,             53,59,61,67,71,73,79,83,89,97,101};   /* int64_t和uint64_t分別表示64位的有符號和無符號整形數 */   /* 在不同位數機器的平臺下通用,都是64位 */   uint64_t ch = 1;      for(int i=0; i<long_str.size(); ++i)   {     ch = ch*primeNum[long_str[i]-'A'];   }    for(int i=0; i<short_str.size(); ++i)   {     if(ch%primeNum[short_str[i]-'A'] != 0)       return false;   }   return true; }  int main() {   string l = "ABCDEFGHIJK";   string s = "ABCDEK";   if(Compare(l, s))     cout << "true" << endl;   else     cout << "false" << endl;   return 0; } 

二、比較兩個字符串是否為變位詞

(1) 問題描述:如果兩個字符串的字符一樣,但是順序不一樣,被認為是兄弟字符串,問如何在迅速匹配兄弟字符串(如,bad和adb就是兄弟字符串)。

(2) 注意:第一點中討論了字符串包含問題,但是不要以為兩個字符串互相包含就是(變位詞)兄弟字符串了,例如aabcde和edcba互相包含,但它們不是變位詞。

(3) 解決方案:

思路一

給每個字母分配一個素數,可以通過判斷兩個字符串的素數乘積是否相等。跟上述素數法一樣,時間復雜度也是O(n+m),需要跟大整數打交道。

/*************************************************************************   > File Name: test3.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<string> #include<stdint.h> //#include<cstdint> // C++11 using namespace std;  bool Compare(string s1, string s2) {   unsigned int primeNum[26] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,             53,59,61,67,71,73,79,83,89,97,101};   uint64_t ch = 1;    for(int i=0; i<s1.size(); ++i)   {     ch = ch*primeNum[s1[i]-'a'];   }    for(int i=0; i<s2.size(); ++i)   {     ch = ch/primeNum[s2[i]-'a'];   }      if(ch == 1)     return true;   else     return false; }  int main() {   string s1 = "abandon";   string s2 = "banadon";   if(Compare(s1, s2))     cout << "They are brother words!" << endl;   else     cout << "They aren't brother words!" << endl;   return 0; } 

思路二

將兩個字符串按照字母表順序排序,看排序后的字符串是否相等,如果相等則是兄弟字符串(變位詞)。這種方法的時間效率根據你使用的排序算法不同而不同。當然,你可以自己寫排序算法,這里我們使用C++的STL中的sort()函數對字符串進行排序。

/*************************************************************************   > File Name: test4.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<algorithm> #include<string> using namespace std;  // 自定義序函數(二元謂詞) bool myfunction(char i, char j) {   return i > j; }  bool Compare(string s1, string s2) {   // 采用泛型算法對s1,s2排序,sort()采用的是快速排序算法   sort(s1.begin(), s1.end(), myfunction);   sort(s2.begin(), s2.end(), myfunction);   if(!s1.compare(s2)) // 相等返回0     return true;   else     return false; }  int main() {   string s1 = "abandon";   string s2 = "banadon";   if(Compare(s1, s2))     cout << "They are brother words!" << endl;   else     cout << "They aren't brother words!" << endl;   return 0; } 

三、字典找出所有變位詞集合(重點)

(1) 問題描述:給定一個英語字典,找出其中的所有變位詞集合。

(2) 解決方案:

思路一

對于這個問題,最快想到的最直接的方法就是針對每一個單詞跟字典中的其他單詞進行比較。然而,假設一次比較至少花費1微秒的時間,則擁有二十萬單詞的字典將花費:200000單詞 x 200000比較/單詞 x 1微秒/比較 = 40000x10^6秒 = 40000秒 ≈ 11.1小時。比較的次數太多了,導致效率低下,我們需要找出效率更高的方法。

思路二

標識字典中的每一個單詞,使得在相同變位詞類中的單詞具有相同的的標識,然后集中具有相同標識的單詞。將每個單詞按照字母表排序,排序后得到的字符串作為該單詞的標識。那么對于該問題的解題過程可以分為三步:第一步,讀入字典文件,對單詞進行排序得到標識;第二步,將所有的單詞按照其標識的順序排序;第三步,將同一個變位詞類中的各個單詞放到同一行中。

這里出現了標識-單詞(key-value)對,我們很容易想到C++中的關聯容器map,使用map的好處就是:

動態管理內存,容器大小動態改變;
單詞與它的標識一一對應,對于相同標識(key)的單詞直接加在值(value)后面;
無需根據標識排序,因為map會自動按關鍵字有序(默認升序)。
所以,在將每個單詞及其標識存入map以后,就可以直接遍歷輸出了,每一個map元素就是一個變位詞集合。

C++實現代碼如下:

/*************************************************************************   > File Name: test5.cpp   > Author: SongLee  ************************************************************************/ #include<iostream> #include<fstream>  // file I/O #include<map>    // map #include<string>   // string #include<algorithm> // sort using namespace std; /*  *map是C++中的關聯容器  *   按關鍵字有序  *   關鍵字不可重復  */ map<string, string> word;  /* 自定義比較函數(用于排序) */ bool myfunction(char i, char j) {   return i < j; }  /*  *對每個單詞排序  *排序后字符串作為關鍵字,原單詞作為值  *存入map中  */ void sign_sort(const char* dic) {   // 文件流   ifstream in(dic);   if(!in)   {     cout << "Couldn't open file: " + string(dic) << endl;     return;   }    string aword;   string asign;   while(in >> aword)   {     asign = aword;     sort(asign.begin(), asign.end(), myfunction);     // 若標識不存在,創建一個新map元素,若存在,加在值后面     word[asign] += aword + " ";   }   in.close(); }  /*  *寫入輸出文件  */ void write_file(const char* file) {   ofstream out(file);   if(!out)   {     cout << "Couldn't create file: " + string(file) << endl;     return;   }      map<string, string>::iterator begin = word.begin();   map<string, string>::iterator end = word.end();   while(begin != end)   {     out << begin->second << "/n";     ++begin;   }   out.close(); }  int main() {   string dic;    string outfile;    cout << "Please input dictionary name: ";   cin >> dic;   cout << "Please input output filename: ";   cin >> outfile;    sign_sort(dic.c_str());   write_file(outfile.c_str());    return 0; } 

附:(2012.5.6百度實習筆試題)一個單詞交換字母位置,可得另一個單詞,如army->mary,成為兄弟單詞。提供一個單詞,在字典中找到它的兄弟。描述數據結構和查詢過程。

解題思路:如果不允許進行預處理,那么我們只能順序遍歷整個字典,計算每個單詞的標識與給定單詞的標識比較。如果允許進行預處理,我們可以如上述思路二將所有單詞加入一個map,然后輸出關鍵字(給定單詞的標識)對應的值,值中就包含了該單詞的所有兄弟單詞。

相信本文所述實例有助于讀者更好的掌握C++下數據結構與算法的實現技巧。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 东方市| 龙陵县| 中山市| 仙游县| 来凤县| 岚皋县| 泰顺县| 霸州市| 镇安县| 两当县| 福清市| 华容县| 贡觉县| 达日县| 合作市| 阿拉尔市| 扶沟县| 江油市| 宜君县| 青川县| 南平市| 治多县| 土默特左旗| 大名县| 东莞市| 沭阳县| 仲巴县| 繁昌县| 鄂伦春自治旗| 嫩江县| 洛宁县| 深圳市| 犍为县| 鹿邑县| 犍为县| 如东县| 银川市| 白山市| 石泉县| 古交市| 安丘市|