本文内容是基于小象学院——林沐 《面试算法 LeetCode 刷题班》,后期仍将对相关内容进行不定期更新!
7. 哈希表与字符串
文章目录
哈希表: 也叫散列表,根据key直接进行访问得数据结构,比较快速。
问题1:当遇上 负数或非常大的整数,字符串,及浮点数,数组,对象等,如何进行哈希映射?
需要利用哈希函数,将关键字值转换为整数再对表长取余,从而关键字值被转换成哈希表的表长范围内的整数。
问题2: 采用上述方法,但不排除地址冲突(不同元素对应同一个地址)的问题,如何解决?
这就采用拉链法,构造哈希表来解决冲突,可以将哈希表设置为单个的指针数组,每一个指针指向的是哈希函数的结果相同的单链表. 插入时采用头插法,搜索时依次遍历.
实现:
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;
}
还可以同 STL 中的 map 包含在
LeetCode 409 最长回文串(E)
给定一个字符串,求该字符串可以生成的最长回文字符串长度.
观察回文字符串的特点,字符串长度为奇数时有中心字符,除了中心字符外,或者字符串长度为偶数长度时,其他字符只要头部出现尾部就要对应出现.
本题思路:
- 利用字符哈希方法,统计字符串中所有字符的数量
- 设置最长回文串偶数字符长度为 max_length = 0
- 设置是否有 中心点 标记 flag = 0
- 遍历每一个字符,字符数为 count ,若 count 为偶数,则 max_length += count; 若 count 为奇数, max_length + = count - 1, flag = 1;
- 最终最长回文字符串长度为 max_length + flag;
解决方法:
class Solution {
public:
int longestPalindrome(string s) {
int char_map[128] = { 0 };
int max_length = 0;
int flag = 0;
for (int i = 0; i < s.length(); i++)
{
char_map[s[i]] += 1;
}
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;
}
}
return max_length + flag;
}
};
LeetCode 290 词语模式(E)
已知字符串pattern 与 字符串str, 确认str是否与 pattern匹配,两者均只包含小写字母,以空格间隔. pattern = “abba”, str = “dog cat cat dog” 则为 true ,反之则为 false.
本题思路:
- 设置单个 单词 到 字符 的映射(哈希), 使用数组used[128]记录 pattern 字符是否使用
- 遍历所有单词,同时对应的向前移动指向 pattern 字符的指针,每拆分一个单词,做逻辑判断 。
- 若单词个数与 pattern 字符个数不匹配,返回 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] == ' ')
{
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 = ""; // 完成一个单词的插入和查询后,清空word
pos++; // 指向pattern字符的pos 指针前移
}
else {
word += str[i]; // 读取字符
}
}
if (pos != pattern.length())
{
return false; // 有多余的pattern字符
}
return true;
}
};
LeetCode 49 同字符词语分组(M)
已知一组字符串,将所有由颠倒字母顺序构成的字放到一起输出。
Example:
Input: ["eat", "tea", "tan", "ate", "nat", "bat"],
Output:
[
["ate","eat","tea"],
["nat","tan"],
["bat"]
]
本题思路1:
设置字符串到字符串向量的哈希表。遍历字符串向量strs中的单词strs[i]:
- 设置临时变量 str=strs[i],对str进行排序
- 若str未出现在anagram中,设置str到一个空字符串向量的映射
- 将strs[i]添加到字符串向量 anagram[str]中
遍历哈希表 anagram,将全部key对应的value push至最终结果中
解决方法:
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
map<string, vector<string>> anagram;
vector<vector<string>> result;
for (int i = 0; i < strs.size(); i++)
{
string str = strs[i];
sort(str.begin(), str.end());
if (anagram.find(str) == anagram.end())
{
vector<string> item;
anagram[str] = item;
}
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;
}
};
本题思路2:
将字符串映射到26个字母的字符数量为 key , 以字符串向量为 value ,剩余过程同上。
解决方法:
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']++;
}
}
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
map<vector<int>, vector<string>> anagram;
vector<vector<string>> result;
for (int i = 0; i < strs.size(); i++)
{
vector<int> vec;
change_to_vector(strs[i], vec);
if (anagram.find(vec) == anagram.end())
{
vector<string> item;
anagram[vec] = item;
}
anagram[vec].push_back(strs[i]);
}
map<vector<int>, vector<string>>::iterator it;
for (it = anagram.begin();it != anagram.end() ; it++)
{
result.push_back((*it).second);
}
return result;
}
};
LeetCode 3 无重复字符的最长字串(M)
本题思路:
- 设置一个记录字符数量的字符哈希, char_map
- 设置一个记录当前满足条件的最长子串变量 word;
- 设置两个指针( i 和 j )指向字符串的第一个字符
- 设置最长满足条件的字串的长度 result
- i 指针向后逐个扫描字符串的字符,使用
char_map记录字符数量,如果word中没有出现该字符,对word尾部添加字符并检查result是否需要更新;否则 j 向前移动,更新char_map的字符数量,直到字符s[i]的数量为1;更新 word ,将 word 赋值为 j 和 i 之间的字串。
解决方法:
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int j = 0;
int 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.push_back(s[i]);
if (result < word.length())
{
result = word.length();
}
}
else
{
while (j<i && char_map[s[i]] >1)
{
char_map[s[j]]--;
j++;
}
}
word = "";
for (int k = j; k <= i; k++)
{
word += s[k];
}
}
return result;
}
};
LeetCode 187 重复的 DNA 序列(M)
将 DNA 序列看作只包含[‘A’,‘C’,‘G’,‘T’] 4个字符的字符串,给一个DNA字符串,找到所有长度为10的,且超过1次的子串。
本题思路1:
依次取出给定的字符串中所有长度为 10 的字符串,然后使用 map 来统计字串出现个数,然后输出,复杂度为 O(n)
解决方法:
class Solution {
public:
vector<string> findRepeatedDnaSequences(string s) {
vector<string> result;
map<string, int> word_map;
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]++;
}
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;
}
};
本题思路2:
????
LeetCode 76 最小窗口子串(H)
已知字符串S与字符串T,求S在的最小窗口(区间),使得这个区间中包含了字符串T中的所有字符。
Example:
Input: S = "ADOBECODEBANC", T = "ABC"
Output: "BANC"
本题思路1:
1.设置两个 字符哈希 数组, map_s 和 map_t代表当前处理的窗口区间中的字符变量,map_t 代表子串 T 的字符数量。
2.设置两个指针( i 和 begin)指向字符第一个字符
3.i 指针向后逐个扫描字符串中的字符,在这个过程中,循环检查 begin 指针是否可以向前移动:
如果当前 begin 指向的字符T中没出现,直接前移 begin;
如果 begin 指向的字符T中出现了,但是当前区间窗口中的该字符数量足够,向前移动 begin, 并更新 map_s
否则不能移动 begin, 跳出检查
4.指针i 每向前扫描一个字符,即检查一下是否可以更新最终结果
在整个过程中,使用 begin 和 i 维护一个窗口, 该窗口中的子串满足题目条件,窗口线性向前滑动,整体复杂度为 O(n)。
解决方法:
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++) {
if (map_s[vec_t[i]] < map_t[vec_t[i]]) // 遍历 t 中字符
{
return false; // 若s出现该字符的数量小于 t 中出现的数量
}
}
return true;
}
string minWindow(string s, string t) {
const int MAX_ARRAY_LEN = 128;
int map_s[MAX_ARRAY_LEN] = { 0 }; //记录 s 字符串各字符个数
int map_t[MAX_ARRAY_LEN] = { 0 }; //记录 t 字符串各字符个数
vector<int> vec_t;
for (int i = 0; i < t.length(); i++)
{
map_t[t[i]]++;
}
for (int i = 0; i < MAX_ARRAY_LEN; i++)
{
if (map_t[i] > 0) {
vec_t.push_back(i); // 遍历,将字符串t中出现的字符存储到 vec_t中
}
}
int window_begin = 0;
string result;
for (int i = 0; i < s.length(); i++)
{
map_s[s[i]]++;
while (window_begin < i)
{
char begin_ch = s[window_begin];
if (map_t[begin_ch] == 0)
{
window_begin++;
}
else if (map_s[begin_ch] > map_t[begin_ch]) {
map_s[begin_ch]--;
window_begin++;
}
else
{
break;
}
}
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;
}
};

本文深入解析LeetCode经典算法题目,涵盖哈希表、字符串处理、动态规划等核心概念,通过实例讲解算法设计思路与优化技巧,适合算法初学者及面试备考者。
465

被折叠的 条评论
为什么被折叠?



