哈希表与字符串
哈希表是根据关键字值(key)直接进行访问的数据结构,它通过把关键字值映射到表中一个位置(数组下标)来直接访问,以加快查找关键字值的速度。这个映射函数叫做哈希函数,存放记录的数组叫做哈希表。
最简单的哈希:字符哈希
#include <iostream>
#include <string>
using namespace std;
int main()
{
int char_map[128]={0};
string str = "abddhsjdsjbqndbnds";
for(int i=0;i<str.length();i++)
char_map[str[i]]++;
for(int i=0;i<128;i++){
if(char_map[i]>0)
printf("[%c][%d]:%d\n",i,i,char_map[i]);
}
return 0;
}
哈希表排序整数:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int random[10]={999,1,444,7,20,9,1,3,7,7};
int char_map[1000]={0};
//string str = "abddhsjdsjbqndbnds";
for(int i=0;i<10;i++)
char_map[random[i]]++;
for(int i=0;i<1000;i++){
for(int j=0;j<char_map[i];j++)
printf("%d\n",i);
}
return 0;
}
时间复杂度是O(表长+n)n为元素个数
以拉链法构造哈希表:
将所有的哈希函数结果相同的结点连接在同一个单链表中。
若选定的哈希表长度为m,则可将哈希表定义为一个长度为m的指针数组t[0…m-1],指针数组中的每个指针指向哈希函数结果相同的单链表。
插入value:
将元素value插入哈希表,若元素value的哈希函数值为hash_key,将value对应的节点以头插法的方式插入到以t[hash_key]为头指针的单链表中。
查找value:
若元素value的哈希函数值为hash_key,遍历以t[hash_key]为头指针的单链表,查找链表各个节点的值域是否为value。
struct ListNode{
int val;
ListNode* next;
ListNode(int x):val(x),next(NULL){}
};
int hash_func(int key,int table_len){
return key%table_len;
}
void insert(ListNode* hash_table[], ListNode* node, int table_len){
int hash_key = hash_func(node->val,table_len);
node->next = hash_table[hash_key];
hash_table[hash_key] = node;
}
bool search(ListNode* hash_table[],int value,int table_len){
int hash_key = hash_func(val,table_len);
ListNode* head = hash_table[hash_key];
while(head){
if(head->val==val)
return true;
head=head->next;
}
return false;
}
int main()
{
const int TABLE_LEN=11;
ListNode *hash_table[] = {0};
vector<ListNode*> hash_node;
int test[8] = {1,1,4,9,20,30,150,500};
for(int i=0;i<8;i++)
hash_node.push_back(new ListNode(test[i]));
for(int i=0;i<hash_node.size();i++)
Insert(hash_table,hash_node[i],TABLE_LEN);
printf("Hash Table:\n");
for(int i=0;i<TABLE_LEN;i++){
printf("[%d]:",i);
ListNode *head = hash_table[i];
while(head){
printf("->%d",head->val);
head = head->next;
}
printf("\n");
}
printf("\n");
for(int i=0;i<10;i++){
if(Search(hash_table,i,TABLE_LEN))
printf("%d is in the hash table.\n",i);
else
printf("%d is not in the hash table.\n",i);
}
//cout << "Hello world!" << endl;
return 0;
}
第一题:最长回文串:leetcode 409
已知一只包括大小写字符的字符串,求用该字符串中的字符可以生成的最长回文字符串长度。
例如 s=“abccccddaa”,可生成的最长回文串字符串长度为9,如dccaaaccd、abccbccda等都对的。
int longestPalindrome(string s){
int char_map[128]={0};
int flag=0;
int sum=0;
for(int i=0;i<s.length();i++){
char_map[s[i]]++;
}
for(int i=0;i<128;i++){
if(char_map[i]%2==0)
sum+=char_map[i];
else {
sum+=char_map[i]-1;
flag=1;
}
}
return sum+flag;
}
第二题:词语模式
已知字符串pattern与字符串str,确认str是否与pattern匹配。str与pattern匹配代表字符串str中的单词与pattern中的字符一一对应。
例如:
pattern=“abba” , str = “dog cat cat dog” 匹配
pattern = “abba”, str = “dog cat cat fish” 不匹配
思路:
1.设置单词(字符串)到pattern字符的映射(哈希);使用数组used[128]记录pattern字符是否使用。
2.遍历str , 按照空格拆分单词,同时对应的向前移动指向pattern字符的指针,每拆分出一个单词,判断:
如果该单词从未出现在哈希表中:
如果当前的pattern字符已被使用,则返回false;
将单词与当前指向的pattern字符做映射;
标记当前指向的pattern字符已使用
否则:
如果当前单词在哈希表中的映射字符不是当前指向的pattern字符,则返回false
3.若单词个数与pattern字符个数不匹配,返回false.
bool wordPattern(string pattern, string str){
map<string,char> word_map;
char used[128]={0};
int pos=0;
string word;
for(int i=0;i<str.length();i++){
if(str==' '){
if(pos==pattern.length()) return false;
if(word_map.find(word)==word_map.end()){
if(used[pattern[pos]]) return false;
word_map[word]=pattern[pos];
used[pattern[pos]]=1; //标记已使用
}
else {
if(word_map[word]!=pattern[pos]) return false;
}
word="";
pos++;
}
else
word+=str[i];
}
if(pos!=pattern.length()) return false;
return true;
}
第三题:通字符词语分组:
已知一组字符串,将所有的anagram(由颠倒字母顺序而构成的字)放到一起输出。leetcode 49
例如:[“eat”,“tea”,“tan”,“ate”,“nat”,“bat”]
返回:[[“ate”,“eat”,“tea”],[“nat”,“tan”],[“bat”]]
思路:
方法一:哈希表以内部进行排序的各个单词为key,以字符串向量<vector>为value,存储各个字符数量相同的字符串(anagram)
设置字符串到字符串向量的哈希表anagram,遍历字符串向量strs中的单词strs[i]:
1.设置临时变量str = strs[i],对str进行排序
2.若str未出现在anagram中,设置str到一个空字符串向量的映射。
3.将strs[i]添加到字符串向量anagram[str]中。
遍历哈希表anagram,将全部key对应的value push至最终结果中。
vector<vector<string>> groupAnagrams(vector<string> & strs){
map<string,vector<string>> Q;
vector<vector<string>> result;
for(int i=0;i<strs.size();i++){
string str=strs[i];
sort(str.begin(),str.end());
if(Q.find(str)==Q.end()){
vector<string> item;
Q[str] = item;
}
Q[str].push_back(str[i]);
}
map<string,vector<string>>::iterator it;
for(it=Q.begin();it!=Q.end();it++){
result.push_back((*it).second);
}
return result;
}
方法二:哈希表以26个字母的字符数量(一个长度为26的vector,统计单词中各个字符的数量)为key,以字符串数量(vector)为value,存储各个字符数量相同的字符串(anagram)
思路:设置vector到字符串向量的哈希表anagram,遍历字符串向量strs中的单词strs[i]:
1.统计strs[i]中各个字符数量,存储至vec。
2.若vec未出现在anagram中,设置vec到一个空字符串向量的映射。
3.将strs[i]添加至字符串向量anagarm中。遍历哈希表anagarm,将全部的key对应的value push至最终结果中。
void change_to_vector(string &str,vector<int>&vec){
for(int i=0;i<26;i++)
vec.push_back(0);
for(int i=0;i<str.length();i++){
vec[str[i]-'a']++;
}
}
vector<vector<string>> groupAnagrams(vector<string> & strs){
map<vector<int>,vector<string>> Q;
vector<vector<string>> result;
for(int i=0;i<strs.size();i++){
vector<int > vec;
change_to_vector(str,vec);
if(Q.find(vec)==Q.end()){
vector<string> item;
Q[vec] = item;
}
Q[vec].push_back(str[i]);
}
map<vector<int>,vector<string>>::iterator it;
for(it=Q.begin();it!=Q.end();it++){
result.push_back((*it).second);
}
return result;
}
第四题:无重复字符的最长子串 leetcode 3
已知一个字符串,求用该字符串的无重复字符的最长子串的长度。
例如:
s = “abcabcbb” ->“abc” 3
s= “bbbb” “b” 1
思路:
1.设置一个记录字符数量的字符哈希,char_map;
2.设置一个记录当前满足条件的最长子串变量word;
3. 设置两个指针(指针i和begin)指向字符串的第一个字符;
4. 设置最长满足条件的子串的长度result;
5. i指针向后逐个扫描字符串中的字符,在这个过程中,使用char_map记录字符数量
如果word中没有出现该字符;对word尾部添加字符并检查result是否需要更新;
否则;bgein指针向前移动,更新char_map中的字符数量,直到字符s[i]的数量为1;更新word,
将word赋值为bgein与i之间的子串。
在整个过程中,使用bgein与i维护一个窗口,该窗口中的子串满足题目条件(无重复的字符),窗口线性向前滑动,整体的时间复杂度为o(n)。
int lengofLongestSubstring(string s){
int result =0 ;
int begin =0;
int char_map[128]={0};
string word;
for(int i=0;i<s.length();i++){
char_map[s[i]]++;
if(char_map[s[i]]==1){
word+=s[i];
if(result<word.length())
result=word.length();
}
else {
if(begin<i && char_map[s[i]]>1){
char_map[begin]--;
begin++;
}
word="";
for(int j=begin;j<=i;j++)
word+=s[j];
}
}
return result;
}
第五题:重复的DNA序列
将DNA序列看做是只包含[‘A’,‘C’,‘G’,‘T’]4个字符二代字符串,给一个DNA字符串,找到所有长度为10的且出现超过一次的子串。
Leetcode 187
s=“AAAAACCCCCQAAAACCCCCAAAAAGGGTTT”
Return:[“AAAAACCCCC”,“CCCCCAAAAA”]
思路:
枚举DNA字符串的中所有长度为10的子串,将其插入到哈希Map中,并记录子串数量;遍历哈希map,将所有出现超过一次的子串存储到结果中。算法复杂度O(n)
vector<string> findRepeatDnaSequences(string s){
map<string,int> word_map;
vector<string> result;
for(int i=0;i<s.length();i++){
string word = s.substr(i,10);
if(word_map.find(word)!=word_map.end())
word_map[word]+=1;
else
word_map[word]=1;
}
map<string,int> ::iterator it;
for(it=word_map.begin();it!=word_map.end();it++){
if(it->second>1)
result.push_back(it->first);
}
return result;
}