算法基础 典型题(七)哈希表 与 字符串

记录算法基础题思路:

最长回文长度:统计字符数量,最长为全部偶数累计和,奇数少放一个,存在奇数就留一个放中间

词语模式匹配:遍历字符串,建立一个str单词和pattern字符的hash表,并且char_flag[256]记录pattern中字符的使用状态,如果出现一个表项建立过程对应字符已被使用,校验失败

anagram分组:建立<sring, vector<string>>每个字符串单元排序结果作为key值,建立map字典,相同key追加到字典中,把字典的second收集到结果;

无重复字符最长子串:hash记录字符存在 + 双指针滑动窗口,滑动右边界直到出现重复字符更新max_len,左边移动窗口重新回到无重复,然后继续右边移动

重复DNA序列:用map<int, bool>来统计出现的类型,把string按位转化为20bit的数据,四种状态占2bit,bool为true表示放入不会重复收集;

最小覆盖子串:滑动窗口 先统计t的全部字符,先移动右边界直到包含t全部字符,再移动左边界直到还存在这些字符,作为一次结果,然后开始找下一轮,统计找到的最小长度结果

step1

最长回文长度:https://leetcode-cn.com/problems/longest-palindrome/submissions/

给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。

    int longestPalindrome(string s) {
        /* 利用hash表统计全部字符数量,然后遍历每一个字符 
            如果为偶数length+=count, 
            如果为奇数length+=count-1, 并且置位中心数flag=1,
            最终结果位length+flag*/
        int c_map[256] = { 0 };
        int center_flag = 0;
        int length = 0;
        for (int i = 0; i < s.length(); i++) {
            c_map[s[i]]++;
        }
        for (int i = 0; i < 256; i++) {
            if (c_map[i] % 2 == 0) {
                length += c_map[i];
            } else {
                length += c_map[i] - 1;
                center_flag = 1;
            }
        }
        return length + center_flag;
    }

词语模式匹配:https://leetcode-cn.com/problems/word-pattern/submissions/

给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。

    bool wordPattern(string pattern, string str) {
        /* 遍历字符串,建立一个str单词和pattern字符的hash表,
            并且记录pattern中字符的使用状态,
            如果出现一个表项建立过程对应字符已被使用,校验失败*/
        bool c_used[256] = { 0 };
        map <string, char> word_map;
        string word;
        int pos = 0;
        str += ' ';//追加一个尾部空格,可以简化遍历条件
        for (int i = 0; i < str.size(); i++) {
            if (str[i] != ' ') {
                word += str[i];
            } else {
                if (pos == pattern.length()) {
                    return false; //pattern长度不匹配
                }
                /* 获取到一个单词,单词表项没有就尝试建立表项 */
                if(word_map.find(word) == word_map.end()) {
                    if (c_used[pattern[pos]] == true) {
                        return false; //对应字符位已经被占用,不能再映射
                    }
                    word_map[word] = pattern[pos];
                    c_used[pattern[pos]] = true;
                } else {
                    if (word_map[word] != pattern[pos]) {
                        return false;//word已经构建hash表项,但映射的key值和当前不同
                    }
                }
                pos++;
                word = "";
            }
        }
        if (pos != pattern.length()) {
            return false; //pattern长度不匹配
        }
        return true;
    }

anagram分组:https://leetcode-cn.com/problems/group-anagrams/submissions/

给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: ["eat", "tea", "tan", "ate", "nat", "bat"]
输出:
[
  ["ate","eat","tea"],
  ["nat","tan"],
  ["bat"]
]

public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        /* 每个字符串单元排序结果作为key值,建立map字典,相同key追加到字典中 */
        map<string, vector<string>> dict;
        vector<vector<string>> res;
        for (auto s : strs) {
            string key = s;
            sort(key.begin(),key.end());
            dict[key].push_back(s);
        }
        for (auto it: dict) {
            res.push_back(it.second);
        }
        return res;
    }

无重复字符最长子串:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/submissions/

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

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        /* hash记录字符存在 + 双指针移动查找非连续子串长度,
        滑动窗口右边界往后移动直到出现重复字符,更新max_len
        左边界移动使 窗口重新回到不存在重复字符的场景,然后继续右边界滑动 */
        bool c_table[256] = {0}; // 记录当前包含哪些字符
        int max_len = 0;
        int left = 0;
        int right = 0;
        while (right < s.length() && left < s.length()) { // 目标是右指针走到末尾
            // right右边移动,直到遇到第一个重复字符
            while (right < s.length() && c_table[s[right]] == false) {
                c_table[s[right]] = true;
                right++;
            }
            // 更新这次非连续遍历的长度
            max_len = max(max_len, right - left);
            // 移动左指针,直到找到相同的字符
            while (left < s.length() && s[left] != s[right]) {
                c_table[s[left]] = false;
                left++;
            }
            // 把相同的字符弹出,有指针也往前移动一格
            left++;
            right++;
        }
        return max_len;
    }
};

重复DNA序列:https://leetcode-cn.com/problems/repeated-dna-sequences/solution/

所有 DNA 都由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:“ACGAATTCCG”。在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助。
编写一个函数来查找 DNA 分子中所有出现超过一次的 10 个字母长的序列(子串)。

class Solution {
public:
    vector<string> findRepeatedDnaSequences(string s) {
        /* 用map<int, bool>来统计出现的类型,把string按位转化为20bit的数据,四种状态占2bit */
        map <int, bool> table; /* 存在表示已放入,value true表示已统计 防重复 */
        vector<string> ret;
        if (s.size() < 10) {
            return ret;
        } 
        for (int i = 0; i < s.size() - 9; i++) {
            int key = get_key(s.substr(i, 10));
            // cout << key << s.substr(i, 10) << i << endl; 
            // 没找到string
            if (table.find(key) == table.end()) {
                table[key] = false; // 新增
            } else {
            // 找到说明存在
                if (table[key] == false) {
                    // 放入后设置true防止重复放入
                    table[key] = true;
                    ret.push_back(s.substr(i, 10));
                }
            }
        }
        return ret;

    }
    int get_key(string str) {
        int key = 0;
        for (int i = 0; i < str.size(); i++) {
            switch(str[i]) {
                case 'A':
                    key += 0;
                    break;
                case 'C':
                    key += 1;
                    break;
                case 'G':
                    key += 2;
                    break;
                case 'T':
                    key += 3;
                    break;                                                            
            }
            key <<= 2; // 左边移动2bit
        }
        return key;
    }
};

最小覆盖子串:https://leetcode-cn.com/problems/minimum-window-substring/submissions/

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"
说明:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。

class Solution {
public:
    string minWindow(string s, string t) {
        /* 滑动窗口 先统计t的全部字符,先移动右边界直到包含t全部字符,再移动左边界直到还存在这些字符,
            作为一次结果,然后开始找下一轮,统计找到的最小长度结果 */
        string ret;
        int t_table[128] = {0}; // 统计t的table
        int s_table[128] = {0}; // 统计s的table
        // 先获取t的table
        for (int i = 0; i < t.size(); i++) {
            t_table[t[i]]++;
        }
        // 滑动窗口找s
        int left = 0;
        int right = 0;
        int min_size = 0;
        while (left < s.size() && right < s.size()) {
            // 先移动right直到全部包含
            while (right < s.size() && check_get_all_char(t_table, s_table) == false) {
                s_table[s[right]]++;
                right++; // right最后走到了包含的下一个;
            }
            // 始终找不到,终止
            if (check_get_all_char(t_table, s_table) == false) {
                break;
            }
            // 再移动left保证最小包含
            while (left < s.size() && check_get_all_char(t_table, s_table) == true) {
                s_table[s[left]]--;
                left++; // left最后走到了不包含的下一个;              
            }
            int tmp_size = right - left + 1;
            if (min_size == 0 || tmp_size < min_size) {
                min_size = tmp_size;
                ret = s.substr(left - 1, tmp_size);
            }
        }
        return ret;
    }
    bool check_get_all_char(int t_table[128], int s_table[128]) {
        for (int i = 0; i < 128; i++) {
            if (t_table[i] > s_table[i]) {
                return false;
            }
        }
        return true;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值