http://acm.uestc.edu.cn/#/problem/show/1092
韓爺的夢
Time Limit: 200/100MS (Java/Others) Memory Limit: 1300/1300KB (Java/Others)
一天,韓爺去百度面試,面試官給了他這么一個問題。
給你2萬個字符串,每個字符串長度都是100,然后把2萬個字符串丟入一個 set< string >g 中,問最終set里含有多少個元素?
g 是一個用來存儲字符串、具有去重功能的容器,即相同字符串在 g 中只能保留一個。
兩個字符串相等,當且僅當,長度一樣且對應位置的字符都一樣。
韓爺前晚沒睡好,隨手寫了一個程序交給面試官,然后就gg了。
#include<iostream> #include<string> #include<set> using namespace std; string s; set<string>g; int main(){ for(int k=1;k<=20000;k++){ cin>>s; g.insert(s); } cout<<g.size()<<endl; return 0; }
韓爺醒來之后,發現這只是一個夢(還好只是個夢)。他回憶起夢中的面試官給他的內存限制和時間限制非常低,這么做肯定過不了,那么,現在你不在夢中,你能解決這個問題么?
Input
單case
每個case有且只有2萬行,每一行包含一個字符串,每行字符串的長度都為100 (樣例除外)
字符集:大寫英文字母(A-Z),小寫英文字母(a-z),數字(0-9)
Output
輸出一個整數,表示最終set里含有多少個元素。
Sample input and output
Sample Input | Sample Output |
---|---|
aaAa aaAa bbbb 1234 bbbb bbbb ee09 |
4 |
Hint
樣例只是樣例,不在test中
注意時間限制和內存限制非常低
思路:這道題目難點在於時間與內存限制很苛刻,一般的方法不能奏效,這里只能采用hash。即把每個字符串hash為一個數字,對數字進行比對,題目就ac了。還有個問題就是,hash函數的選取。我第一次選的hash函數就產生了沖突,這個可以多次選擇進行測試,也可以直接采用更復雜的hash函數。我偷懶了下,選的是前者的方法,第二發就ac了。
這里說下關於hash的知識:
求一個字符串的hash值:
假設我們取p=13 ,mod=101
先把abc映射為一個整數
hash[0]=1,表示 a 映射為1
hash[1]=(hash[0]*p+idx(b))%mod=15,表示 ab 映射為 15
hash[2]=(hash[1]*p+idx(c))%mod=97
這樣,我們就把 abc 映射為 97 這個數字了。
hash值呢?
unsigned long long hash[N];
定義一個unsigned long long類型的變量,它的范圍是在[0, 2^64) 內,這就相當於,當數超不過2^64-1后,它會溢出!這就相當於一個數模2^64的過程。
那么hash函數可以理解為:
hash[i]=(hash[i-1]*p)%(2^64)
P取一個大素數,一般習慣取1e9+7或1e9+9
安全指數:三星(所以並不是很安全)
這個之前已經提到過了。
hash[i]=(hash[i-1]*p+idx(s[i]))%mod
hash1[i]=(hash1[i-1]*p+idx(s[i]))%mod1
hash2[i]=(hash2[i-1]*p+idx(s[i]))%mod2
pair<hash1,hash2>表示一個字符串!
解釋:
hash1[i]=(hash1[i-1]*p+idx(s[i]))%mod1
hash2[i]=(hash2[i-1]*p+idx(s[i]))%mod2
mod1一般取1e9+7,mod2一般取1e9+9為什么這么取?
1000000007和1000000009是一對孿生素數,取它們,沖突的概率極低!
但請注意,hash的維度越高,耗時越高,耗內存越大!一般情況下,single hash可以被hack掉,但double hash極難被hack掉, 用double hash足以解決問題