首先,你可以問面試官,構成字符串的字符集有多大?是ASCII字符,還是隻是26個字母? 還是有更大的字符集,對於不同的情況,我們可能會有不同的解決方案。
如果我們假設字符集是ASCII字符,那麼我們可以開一個大小爲256的bool數組來表徵每個字 符的出現。數組初始化爲false,遍歷一遍字符串中的字符,當bool數組對應位置的值爲真, 表明該字符在之前已經出現過,即可得出該字符串中有重複字符。否則將該位置的bool數組 值置爲true。代碼如下:
bool isUnique1(string s)
{
bool a[256];
memset(a, 0, sizeof(a));
int len = s.length();
for(int i=0; i < len; ++i)
{
int v = (int)s[i];
if(a[v]) return false;
a[v] = true;
}
return true;
}
該算法的時間複雜度爲O(n)。我們還可以通過位運算來減少空間的使用量。 用每一位表徵相應位置字符的出現。對於ASCII字符,我們需要256位,即一個長度爲8的int 數組a即可。這裏的關鍵是要把字符對應的數字,映射到正確的位上去。比如字符'b'對應的 代碼是98,那麼我們應該將數組中的哪一位置爲1呢?用98除以32,得到對應數組a的下標: 3。98對32取模得到相應的位:2。相應代碼如下:
bool isUnique2(string s)
{
int a[8];
memset(a, 0, sizeof(a));
int len = s.length();
for(int i=0; i < len; ++i)
{
int v = (int)s[i];
int idx = v/32, shift=v%32;
if(a[idx] & (1 << shift)) return false;
a[idx] |= (1 << shift);
}
return true;
}
兩個算法的本質其實是一樣的,只不過一個用bool單元來表徵字符出現,一個用位來表徵。
完整代碼如下:
#include <iostream>
#include <cstring>
using namespace std;
bool isUnique1(string s)
{
bool a[256];
memset(a, 0, sizeof(a));
int len = s.length();
for(int i=0; i < len; ++i)
{
int v = (int)s[i];
if(a[v]) return false;
a[v] = true;
}
return true;
}
bool isUnique2(string s)
{
int a[8];
memset(a, 0, sizeof(a));
int len = s.length();
for(int i=0; i < len; ++i)
{
int v = (int)s[i];
int idx = v/32, shift=v%32;
if(a[idx] & (1 << shift)) return false;
a[idx] |= (1 << shift);
}
return true;
}
int main()
{
string s1 = "i am hawstein.";
string s2 = "abcdefghijklmnopqrstuvwxyzABCD1234567890";
cout << isUnique1(s1) << " " << isUnique1(s2) << endl;
cout << isUnique2(s1) << " " << isUnique2(s2) << endl;
return 0;
}
如果字符集只是a-z(或是A-Z),那就更好辦了,用位運算只需要一個整型數即可。
bool isUnique3(string s)
{
int check = 0;
int len = s.length();
for(int i=0; i < len; ++i)
{
int v = (int)(s[i]-'a');
if(check & (1 << v)) return false;
check |= (1 << v);
}
return true;
}