摘要:當遇到C語言庫沒有字符串哈希表的時候,該如何進行操做。
有考C語言可信編程認證的同事常常會問到,C語言庫沒有字符串哈希表操做,那考試遇到了怎麼辦。雖然歷次考試的題目中沒有必需要用到C語言哈希表的題目(至少我都能用常規C作出來),可是還須要防患未然,這裏給出一道有表明性的題目,能夠嘗試作作看:leetcode-cn.com/problems/su…html
給定一個字符串 s 和一些長度相同的單詞 words。找出 s 中剛好能夠由 words 中全部單詞串聯造成的子串的起始位置。
注意子串要與 words 中的單詞徹底匹配,中間不能有其餘字符,但不須要考慮 words 中單詞串聯的順序。
示例:
輸入:
s = "barfoothefoobarman",
words = ["foo","bar"]
輸出:[0,9]
解釋:
從索引 0 和 9 開始的子串分別是 "barfoo" 和 "foobar" 。
輸出的順序不重要, [9,0] 也是有效答案。複製代碼
這題不考慮編程語言的話,用哈希表會比較簡單,那要是用C語言的話,能夠本身擼個哈希表用,對付這類題目仍是綽綽有餘的。算法
思路的話參考leetcode-cn.com/problems/su…中的解法二,這裏只講下怎麼最簡單構造一個哈希表。編程
首先是選取哈希函數,這裏我用的是djb2算法,參考www.cse.yorku.ca/~oz/hash.ht…,碰撞率至關低,分佈平衡,實現也很簡單,就兩三行代碼,記住關鍵數字(5381和33)。數組
If you just want to have a good hash function, and cannot wait, djb2 is one of the best string hash functions i know. it has excellent distribution and speed on many different sets of keys and table sizes.
Language- 代碼bash
unsigned long
hash(unsigned char *str)
{
unsigned long hash = 5381;
int c;
while (c = *str++)
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
return hash;
}複製代碼
有了字符串哈希函數,就可以將大串字符串轉換成數字,數字進而能夠做爲數組的下標(key)存儲信息。那麼哈希表的大小怎麼取呢?通常大小要大於存儲的數據個數,好比最多100個數據,存到哈希表的話大小確定要大於100才行。對於這題而言,沒有明確告訴你單詞的最大個數,只能估值了,這裏通過幾輪提交測試,獲得哈希表大小與經過用例個數的關係,說明這道題目最多的單詞數可能在300左右,平均個數<50個吧:編程語言
5 -> 110/173
10 -> 143/173
50 -> 170/173
100 -> 170/173
300 -> 172/173
400 -> 173/173複製代碼
這裏給出個人解答:函數
C 代碼測試
// 字符串最大值,hash表大小,估值和實際數據個數有關
#define MAXWORDCOUNT 1000
static int wordCount[MAXWORDCOUNT];
static int currWordCount[MAXWORDCOUNT];
// ref: http://www.cse.yorku.ca/~oz/hash.html
unsigned long DJBHash(const char* s, int len) {
unsigned long hash = 5381; // 經驗值,hash衝突機率低,分佈平衡
while (len--) {
hash = (((hash << 5) + hash) + *(s++)) % MAXWORDCOUNT; /* hash * 33 + c */
}
return hash;
}
int* findSubstring(char * s, char ** words, int wordsSize, int* returnSize){
memset(wordCount, 0, sizeof(wordCount));
*returnSize = 0;
const int kSLen = strlen(s);
if (kSLen == 0 || wordsSize == 0) return NULL;
const int kWordLen = strlen(words[0]);
// 將單詞數量存到哈希表中,key: word, value: 單詞數量
for (int i = 0; i < wordsSize; ++i)
++wordCount[DJBHash(words[i], kWordLen)];
int *result = malloc(sizeof(int) * kSLen);
for (int i = 0; i < kWordLen; ++i) {
for (int j = i; j + kWordLen * wordsSize <= kSLen; j += kWordLen) {
// 統計當前窗口的單詞數量
for (int k = (j == i ? 0 : wordsSize - 1); k < wordsSize; ++k)
++currWordCount[DJBHash(s + j + k * kWordLen, kWordLen)];
// 判斷兩個哈希表是否相等,即窗口中的單詞是否和給定詞典徹底匹配
if (memcmp(wordCount, currWordCount, sizeof(wordCount)) == 0)
result[(*returnSize)++] = j;
--currWordCount[DJBHash(s + j, kWordLen)];
}
// 哈希表清零操做
memset(currWordCount, 0, sizeof(currWordCount));
}
return result;
}複製代碼