欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++變位詞問題分析

 更新時(shí)間:2014年08月14日 10:18:14   投稿:shichen2014  
這篇文章主要介紹了C++變位詞問題分析,非常經(jīng)典的算法,對(duì)于進(jìn)行C++下的算法設(shè)計(jì)有很大的啟發(fā)性,需要的朋友可以參考下

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

一、字符串包含問題

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

(2) 舉例:字符串1為ABCDEFGHIJK,字符串2為ABCDE,則字符串1包含字符串2,因?yàn)樽址?中包含的字母在字符串1中也都有。

(3) 解決方案:

思路一

最直接的思路就是針對(duì)字符串2中的每個(gè)字符,輪詢字符串1進(jìn)行比較,看是否在字符串1里面。很明顯這種的時(shí)間效率為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; 
}

思路二

這里由于假定了字符串只包含字母,所以我們可以用一個(gè)額外的數(shù)組flag[26]作為26個(gè)字符標(biāo)識(shí)位,先遍歷長字符串將對(duì)應(yīng)的標(biāo)識(shí)位置1,再遍歷短字符串,如果對(duì)應(yīng)的標(biāo)示位都是1,則包含;否則不包含。這種方法的時(shí)間復(fù)雜度為O(n+m),為了提高空間效率,這里不使用數(shù)組而使用26個(gè)bit位來作為標(biāo)示位(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; 
}

這種方法還可以進(jìn)行優(yōu)化,例如如果長字串的前綴就為短字串,那么我們可以不需要n+m次,而只需要2m次。具體實(shí)現(xiàn)請(qǐng)自己思考。

思路三

給每個(gè)字母分配一個(gè)素?cái)?shù),從2開始到3,5,7...遍歷長字串,求得每個(gè)字符對(duì)應(yīng)素?cái)?shù)的乘積。然后遍歷短字串,判斷該乘積能否被短字符串中的字符對(duì)應(yīng)的素?cái)?shù)整除,如果除的結(jié)果存在余數(shù),則說明有不匹配的字母;如果整個(gè)過程都沒有余數(shù),則說明短字串中的字母在長字串里都有。這種方法的時(shí)間復(fù)雜度也是O(n+m),需要26個(gè)額外空間存素?cái)?shù),但是這種方法有一個(gè)缺點(diǎn)就是需要跟大整數(shù)打交道,因?yàn)槌朔e可能非常大。(這里我們使用<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位的有符號(hào)和無符號(hào)整形數(shù) */ 
  /* 在不同位數(shù)機(jī)器的平臺(tái)下通用,都是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; 
} 

二、比較兩個(gè)字符串是否為變位詞

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

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

(3) 解決方案:

思路一

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

/************************************************************************* 
  > 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; 
} 

思路二

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

/************************************************************************* 
  > File Name: test4.cpp 
  > Author: SongLee 
 ************************************************************************/ 
#include<iostream> 
#include<algorithm> 
#include<string> 
using namespace std; 
 
// 自定義序函數(shù)(二元謂詞) 
bool myfunction(char i, char j) 
{ 
  return i > j; 
} 
 
bool Compare(string s1, string s2) 
{ 
  // 采用泛型算法對(duì)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; 
} 

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

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

(2) 解決方案:

思路一

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

思路二

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

這里出現(xiàn)了標(biāo)識(shí)-單詞(key-value)對(duì),我們很容易想到C++中的關(guān)聯(lián)容器map,使用map的好處就是:

動(dòng)態(tài)管理內(nèi)存,容器大小動(dòng)態(tài)改變;
單詞與它的標(biāo)識(shí)一一對(duì)應(yīng),對(duì)于相同標(biāo)識(shí)(key)的單詞直接加在值(value)后面;
無需根據(jù)標(biāo)識(shí)排序,因?yàn)閙ap會(huì)自動(dòng)按關(guān)鍵字有序(默認(rèn)升序)。
所以,在將每個(gè)單詞及其標(biāo)識(shí)存入map以后,就可以直接遍歷輸出了,每一個(gè)map元素就是一個(gè)變位詞集合。

C++實(shí)現(xiàn)代碼如下:

/************************************************************************* 
  > 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++中的關(guān)聯(lián)容器 
 *   按關(guān)鍵字有序 
 *   關(guān)鍵字不可重復(fù) 
 */ 
map<string, string> word; 
 
/* 自定義比較函數(shù)(用于排序) */ 
bool myfunction(char i, char j) 
{ 
  return i < j; 
} 
 
/* 
 *對(duì)每個(gè)單詞排序 
 *排序后字符串作為關(guān)鍵字,原單詞作為值 
 *存入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); 
    // 若標(biāo)識(shí)不存在,創(chuàng)建一個(gè)新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百度實(shí)習(xí)筆試題)一個(gè)單詞交換字母位置,可得另一個(gè)單詞,如army->mary,成為兄弟單詞。提供一個(gè)單詞,在字典中找到它的兄弟。描述數(shù)據(jù)結(jié)構(gòu)和查詢過程。

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

相信本文所述實(shí)例有助于讀者更好的掌握C++下數(shù)據(jù)結(jié)構(gòu)與算法的實(shí)現(xiàn)技巧。

相關(guān)文章

最新評(píng)論