分门别类刷leetcode——哈希表与字符串(C++实现)

目录

复习哈希表

使用哈希统计字符串中的字符个数

使用哈希排序数组中的元素

解决哈希冲突的方法——拉链法

leetcode 409 最长回文串

leetcode 290 单词模式

leetcode 49 字母异位词分组

leetcode 3 无重复字符的最长子串

leetcode 187 重复的DNA序列

leetcode 76 最小覆盖子串


复习哈希表

 

 

 

使用哈希统计字符串中的字符个数

#include<stdio.h>
#include<string>

int main(int argc, char *argv[]) {
	int char_map[128] = { 0 };
	std::string str = "abcdefgaaxxy";
	for (auto i : str) {
		char_map[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<stdio.h>
#include<string>

int main(int argc, char *argv[]) {
	int random[10] = { 99,1,444,7,20,1,3,7,7 ,9};
	int hash_map[1000] = { 0 };
	for (auto i : random) {
		hash_map[i]++;
	}
	for (int i = 0; i < 1000; i++) {
		for (int j = 0; j < hash_map[i]; j++) {
			printf("%d\n", i);
		}
	}
	return 0;
}

思考:

 

 

 

解决哈希冲突的方法——拉链法

代码实现:

#include<stdio.h>
#include<vector>

struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x):val(x),next(nullptr){}
};

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(value, table_len);
	ListNode *head = hash_table[hash_key];
	while (head) {
		if (head->val == value) {
			return true;
		}
		head = head->next;
	}
	return false;
}

int main(int argc, char *argv[]) {
	const int TABLE_LEN = 11;
	ListNode *hash_table[TABLE_LEN] = { 0 };
	std::vector<ListNode*>hash_node_vec;
	int test[8] = { 1,1,4,9,20,30,150,500 };
	for (auto i : test) {
		hash_node_vec.push_back(new ListNode(i));
	}
	for (auto i : hash_node_vec) {
		insert(hash_table, 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");
	printf("Test search:\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);
		}
	}
	return 0;
}

 

 

 

leetcode 409 最长回文串

给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。

在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。

注意:
假设字符串的长度不会超过 1010。

示例 1:

输入:
"abccccdd"

输出:
7

解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。

思考:

利用哈希统计字符,出现次数为奇数的字符放在中间,偶数的放在两侧。设置一个标记变量flag,若存在出现次数为奇数的字符,则flag的值设置为1,表明其中的一个出现次数为奇数的字符可以作为字符串的中心。

class Solution {
public:
    int longestPalindrome(string s) {
        if(s.length()==0) return 0;
        
        int char_map[128]={0};
        int max_length=0;
        int flag=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){
                max_length+=char_map[i];
            }else{
                //奇数个,则丢弃一个字符
                max_length+=char_map[i]-1;
                flag=1;
            }
        }
        //若存在出现次数为奇数的字符,则flag的值为1,
        //表明其中的一个出现次数为奇数的字符可以作为字符串的中心
        return max_length+flag;
    }
};

 

 

 

leetcode 290 单词模式

给定一种 pattern(模式) 和一个字符串 str ,判断 str 是否遵循相同的模式。

这里的遵循指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应模式。

示例1:

输入: pattern = "abba", str = "dog cat cat dog"

输出: true

示例 2:

输入:pattern = "abba", str = "dog cat cat fish"

输出: false

示例 3:

输入: pattern = "aaaa", str = "dog cat cat dog"

输出: false

示例 4:

输入: pattern = "abba", str = "dog dog dog dog"

输出: false

思路:

class Solution {
public:
    bool wordPattern(string pattern, string str) {
        map<string, char>word_map;
        char used[128]={0};
        string word;
        int pos=0;
        str.push_back(' ');
        for(int i=0; i<str.length(); i++){
            if(str[i]==' '){
                //pattern的长度比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{
                    //当前Word已经建立映射,但是无法与pattern对应
                    if(word_map[word]!=pattern[pos]){
                        return false;
                    }
                }
                word="";
                pos++;
            }else{
                word+=str[i];
            }
        }
        //有多余的pattern字符
        if(pos!=pattern.length()){
            return false;
        }
        return true;
    }
};

 

错误的思路:

用map,以str中的字母和pattern中的单词作为键值对。最后map的尺寸如果和str中字母的种数相同,则匹配。

结果:

失败了,因为map中对于已存在的键,他在做insert操作的时候,会直接忽略该值。下面代码中helper中只有“a,dog”和“b,cat”这两对,后面的lll和fish并没有被存入。

#include<vector>
#include<iostream>
#include<map>
#include<set>
using namespace std;

void wordPattern(string pattern, string str) {
	map<char, string>helper;
	set<char>character;
	vector<string>container;
	string temp = "";
	for (int i = 0; i < str.length(); i++) {
		if (str[i] == ' ') {
			container.push_back(temp);
			temp = "";
		}
		else {
			temp.append(1, str[i]);
		}
	}
	container.push_back(temp);
	if (pattern.length() != container.size()) return;

	//helper中只有“a,dog”和“b,cat”这两对,后面的lll和fish并没有被存入
	for (int i = 0; i < pattern.length(); i++) {
		helper.insert(make_pair(pattern[i], container[i]));
		int a = 0;
	}

}

int main(int argc, char *argv[]) {
	string pattern = "abba";
	string str = "dog cat lll fish";
	wordPattern(pattern, str);
	return 0;
}

 

 

 

leetcode 49 字母异位词分组

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。

示例:

输入: ["eat", "tea", "tan", "ate", "nat", "bat"],输出:
[  ["ate","eat","tea"],  ["nat","tan"],  ["bat"]  ]

说明:

  • 所有输入均为小写字母。
  • 不考虑答案输出的顺序。

思路:

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        vector<vector<string>>result;
        map<string, vector<string>>anagram;
        for(int i=0; i<strs.size(); i++){
            string str=strs[i];
            sort(str.begin(), str.end());
            //哈希表中不存在该组合
            if(anagram.find(str)==anagram.end()){
                //创建vector
                vector<string>item;
                anagram[str]=item;
            }
            //将字符串存入对应vector中
            anagram[str].push_back(strs[i]);
        }
        map<string, vector<string>>::iterator it;
        for(it=anagram.begin(); it!=anagram.end(); it++){
            result.push_back((*it).second);
        }
        return result;
    }
};

 

 

 

leetcode 3 无重复字符的最长子串

给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

思路:

使用两根指针begin i 在字符串上从左向右滑动,保持两根指针之间的每个字符都只出现一次。用一个string类型的变量 Word记录两根指针之间的子串。

用一个哈希表记录两根指针之间的字符以及每个字符出现的次数。

i 向右滑动:

  • 如果 i 所指的字符在begin i 之间没出现过,则将该字符追加到Word后面。
  • 如果 i 指向的字符在begin i 之间出现过,则将begin向右滑动,直到当前 i 指针指向的字符出现次数为 1 时为止,同时更新Word中记录的子串。

用一个string类型的变量 result 去记录最长的子串,遍历的时候不断更新 result 的值遍历完整个字符串时,result 上记录的就是最长子串了。

int lengthOfLongestSubstring(string s) {
	int begin = 0, result = 0;
	string word = "";
	int char_map[128] = { 0 };
	for (int i = 0; i < s.length(); i++) {
		char_map[s[i]]++;
		if (char_map[s[i]] == 1) {
			//将只出现过一次的Word追加到Word尾部
			word += s[i];
			//更新最长子串的值
			if (result < word.length()) {
				result = word.length();
			}
		} else {
			//当前字符出现次数超过一次,并且当前i指向的字符出现次数超过一次
			while (begin < i && char_map[s[i]]>1) {
				//产出哈希表中begin所指向的字符
				char_map[s[begin]]--;
				//移动begin指针,知道char_map[s[i]]中字符出现一次为止
				begin++;
			}
			//清空Word的记录
			word = "";
			//将跟新后的begin和i之间的字符存入Word中
			for (int j = begin; j <= i; j++) {
				word += s[j];
			}
		}
	}
	return result;
}

 

 

 

leetcode 187 重复的DNA序列

所有 DNA 由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。

编写一个函数来查找 DNA 分子中所有出现超过一次的10个字母长的序列(子串)。

示例:

输入: s = "AAAAACCCCCAAAAACCCCCCAAAAAGGGTTT"

输出: ["AAAAACCCCC", "CCCCCAAAAA"]

思路:

方法一:使用map记录字符串中每一个长度为10的子串出现的次数。以string为键,以string出现的次数为值。

vector<string> findRepeatedDnaSequences(string s) {
	vector<string>result;
	if (s.length() < 10) return result;
	map<string, int>container;

	//查看每个长度为10的子串
	for (int i = 0; i < s.length(); i++) {
		string word = s.substr(i, 10);
		if (container.find(word) != container.end()) {
			container[word]++;
		} else {
			container[word] = 1;
		}
	}

	for (auto i : container) {
		if (i.second > 1) {
			result.push_back(i.first);
		}
	}
	return result;
}

 

方法二:位运算

class Solution {
public:
	vector<string> findRepeatedDnaSequences(string s) {
		vector<string>result;
		if (s.length() < 10) return result;

		//每次调用都需要更新全局数组
		for (int i = 0; i < 1048576; i++) {
			g_hash_map[i] = 0;
		}

		int char_map[128] = { 0 };
		char_map['A'] = 0;//00
		char_map['C'] = 1;//01
		char_map['G'] = 2;//10
		char_map['T'] = 3;//11

		int key = 0;
		//存储前10个字符
		//输入 AACCTTGGAA TTCCGGAATTCCGG    输入的是AAGGTTCCAA
		//00 00 10 10 11 11 01 01 00 00 是最终存储的值  AA GG TT CC AA
		for (int i = 9; i >= 0; i--) {
			key = (key << 2) + char_map[s[i]];
		}
		g_hash_map[key] = 1;
		for (int i = 10; i < s.length(); i++) {
			//00 00 00 10 10 11 11 01 01 00
			//AA GG TT CC A
			key = key >> 2;
			//11 00 00 10 10 11 11 01 01 00
			//T AA GG TT CC A
			key = key | (char_map[s[i]] << 18);
			g_hash_map[key]++;
		}
		for (int i = 0; i < 1048576; i++) {
			if (g_hash_map[i] > 1) {
				result.push_back(change_in_to_DNA(i));
			}
		}
		return result;
	}
};

 

 

 

leetcode 76 最小覆盖子串

给定一个字符串 S 和一个字符串 T,请在 S 中找出包含 T 所有字母的最小子串。

示例:

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

说明:

  • 如果 S 中不存这样的子串,则返回空字符串 ""
  • 如果 S 中存在这样的子串,我们保证它是唯一的答案。

思路:

类似于leetcode 3,使用两根指针 begin i ,从字符串s的起始位置出发向右滑动。

使用两个数组map_smap_t

  • map_s——存储当前窗口中的每个字符从数量
  • map_t——存储 t 中的每个字符出现的次数

i 指针先向右逐个扫描s中的字符,在 i 移动的过程中,不断去判断当前 i 指向的字符能否出发指针 begin 的移动。

触发begin移动的条件(begin不能超过  i ):

  • 当前begin指向的字符在 t 中没出现过,则向右移动begin指针
  • 当前begin指向的字符(一定是t中的某个字符)在两个指针之间出现的次数大于该字符在t中出现的次数,则向右移动begin指针
class Solution {
public:
	bool is_window_ok(int map_s[], int map_t[], vector<int>&vec_t) {
		for (int i = 0; i < vec_t.size(); i++) {
			//如果该字符在map_s中出现的次数少于在map_t中出现的次数
			if (map_s[vec_t[i]] < map_t[vec_t[i]])
				return false;
		}
		return true;
	}

	string minWindow(string s, string t) {
		const int MAX_ARRAY_LEN = 128;//char 0-127.利用数组下标记录字符个数
		int map_t[MAX_ARRAY_LEN] = { 0 };//记录t字符串各个字符的个数
		int map_s[MAX_ARRAY_LEN] = { 0 };//记录s字符串各个字符的个数

		vector<int>vec_t;//记录t字符串有哪些字符
		for (auto i : t)   //遍历t,记录t字符串中的各个字符的个数
			map_t[i]++;

		for (int i = 0; i < MAX_ARRAY_LEN; i++) {  //将t中出现的字符存储在vec_t中
			if (map_t[i] > 0)
				vec_t.push_back(i);
		}

		int window_begin = 0;//最小窗口的起始指针
		string result;
		for (int i = 0; i < s.length(); i++) {
			map_s[s[i]]++;
			//确保begin不能超过i
			while (window_begin < i) {
				char begin_ch = s[window_begin];
				//begin指向的字符在t中没出现过
				if (map_t[begin_ch] == 0) {
					//向右移动begin指针
					window_begin++;
				}
				//begin指向的字符在t中出现过,但是begin指向的字符出现的次数在窗口中超过t中出现的次数
				else if (map_s[begin_ch] > map_t[begin_ch]) {
					map_s[begin_ch]--;
					window_begin++;
				}
				else {
					break;
				}
			}
			//如果map_s中各个字符都满足map_t中的字符要求
			if (is_window_ok(map_s, map_t, vec_t)) {
				int new_window_len = i - window_begin + 1;
				if (result == "" || result.length() > new_window_len) {
					result = s.substr(window_begin, new_window_len);
				}
			}
		}
		return result;
	}
};

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值