记录算法基础题思路:
最长回文长度:统计字符数量,最长为全部偶数累计和,奇数少放一个,存在奇数就留一个放中间
词语模式匹配:遍历字符串,建立一个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;
}
};