字母异位词分组
- 首先遍历字符串数组
- 在遍历的过程中对每一个字符串进行排序
- 排序完成之后将字符串添加进
map中, 它的键是排序完成的字符串,它的值是一个数组
所以我们可以写出下面的程序:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> hash;
for (auto str : strs) {
string key = str;
sort(key.begin(), key.end());
hash[key].push_back(str);
}
vector<vector<string>> ans;
for (auto item : hash) {
ans.push_back(item.second);
}
return ans;
}
这道题的关键就是我们需要知道这个异位词在排序之后都是一样的,其次查找相同的词可以通过hash函数的方法来做。
反转字符串中的单词
这个题目的题意是需要我们按照单词作为单位来做分隔,那么如何按照单词级别的单位进行分割呢,其实当我们遍历到空格的时候可以停下来,这样就是一个单词了。
其实这是一个模版,这个模版如下:
string str;
for (int i = 0; i < str.size(); i++) {
while (j < s.size() && str[i] == ' ') j++; //跳过空格
while (i < s.size() && str[i] != ' ') i++; //找到单词
}
有了这个模版我们就可以切分每一个单词了,一旦切分单词了,那我们剩下来的事情就比较好弄了。
- 我们将分割之后的每个单词给反转,单词的区间是
[i, j] - 反转之后我们将新的单词给拷贝一下,定义一个k, 因为可能出现单词位置的改变,比如" hello,world "
- 然后将k之后的字符串全部删除,还是因为出现字符串位置的改变
- 然后将整个字符串反转一下
string reverseWords(string s) {
int k = 0;
for (int i = 0; i < s.size(); i++) {
while (i < s.size() && s[i] == ' ') i++;
if (i == s.size()) break;
int j = i;
while (j < s.size() && s[j] != ' ') j++;
reverse(s.begin() + i, s.begin() + j);
//每次拿完之后, 填充空格
if (k) s[k++] = ' ';
while (i < j) s[k++] = s[i++];
}
s.erase(s.begin() + k, s.end());
reverse(s.begin(), s.end());
return s;
}
比较版本号
这个比较版本号的题目和上面的题做法类似,都是分割字符串,这道的题目的解法如下:
int i, j;
while (i < vesion1.size() || j < version2.size()) {
while (x < version1.size() && version1[i] == '.') x++;
while (y < version2.size() && version2[j] == '.') y++;
}
通过上面的模版我们分离了字符串,然后就是对字符串进行比较,我们比较的手法是将字符串转化成为对应的数字:
//i是初始位置
//转化字符串的区间是[i, x-j]
int a = i == x ? 0 : atoi(version1.substr(i, x - i).c_str());
int b = j == y ? 0 : atoi(version2.substr(j, y - j).c_str());
然后判断是否相同:
if (a > b) return 1;
if (a < b) return -1;
i = x + 1;
j = y + 1;
独特的电子邮件地址
这道题目依然是解决对应的截取对应的字符串,只不过这次使用到的是两个库函数。分别是find函数和对应的insert函数。
str.find('@')
然后直接给出对应的代码:
int numUniqueEmails(vector<string>& emails) {
unordered_set<string> h;
for (auto email : emails) {
int at = email.find('@');
string name;
for (auto c : email.substr(0, at)) {
if (c == '+') break;
else if (c != '.') name += c;
}
string domain = email.substr(at + 1);
h.insert(name + '@' + domain);
}
return h.size();
}
实现 Trie (前缀树)
下面这道题目也是一个比较经典的数据结构叫做trie树,这个trie树主要是用来存储字符串或者说是单词的。 然后这个trie树是下面这样的:
a -> b -> c
|
d
|
c
a是所有树的根节点,当我们遍历这棵树的时候会得到两个单词,分别是abc和对应的adc,当然如果b如果是一个单词的结尾的话,我们我们也可以说ab也是一个单词。
如果用算法来描述这个数据结构的话,代码如下:
struct Node {
bool is_end; //表示一个单词是不是以当前字母结尾
Node* son[26]; //表示一个树的子节点
Node() {
is_end = false;
for (int i = 0; i < 26; i++) {
son[i] = NULL;
}
}
}* root;
当有了上面这个结构的时候我们就可以进行构造树了。主要是插入和查询。
void insert(string word) {
auto p = root;
for (auto c : word) {
int u = c - 'a';
if (p->son[u] == NULL) p->son[u] = new Node();
p = p->son[u];
}
p->is_end = true;
}
bool search(string word) {
auto p = root;
for (auto c : word) {
int u = c - 'a';
if (p->son[u] == NULL) return false;
p = p->son[u];
}
return p->is_end;
}