Skip to content

Commit a0b812f

Browse files
authored
Merge pull request #29 from colorbox/387
387. First Unique Character in a String
2 parents d4ed7ed + 1fdb873 commit a0b812f

File tree

8 files changed

+298
-0
lines changed

8 files changed

+298
-0
lines changed

387/step1.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
Solve Time: 4:20
3+
4+
Time: O(n)
5+
Space: O(1)
6+
7+
一回の走査で解けないかを軽く模索したが思いつかなかったので愚直にmapで記録し、二回ループする手法で解いた。
8+
一回の走査で解くことに固執しかけて時間を無駄に思想になっていたため、そこに気づけた点が個人的によかった
9+
*/
10+
class Solution {
11+
public:
12+
int firstUniqChar(string s) {
13+
map<char, int> character_to_count;
14+
for (const auto& c : s) {
15+
character_to_count[c]++;
16+
}
17+
for (int i = 0; i < s.size() ; i++) {
18+
if (character_to_count[s[i]] == 1) {
19+
return i;
20+
}
21+
}
22+
return -1;
23+
}
24+
};

387/step2_1.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
Time: O(n)
3+
Space: O(1)
4+
5+
step1の改良、マジックナンバーを定数にした
6+
*/
7+
class Solution {
8+
public:
9+
int firstUniqChar(string s) {
10+
map<char, int> character_to_count;
11+
for (const auto& c : s) {
12+
character_to_count[c]++;
13+
}
14+
for (int i = 0; i < s.size(); i++) {
15+
if (character_to_count[s[i]] == 1) {
16+
return i;
17+
}
18+
}
19+
20+
int const kNotFound = -1;
21+
return kNotFound;
22+
}
23+
};

387/step2_2.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
Time : O(N log N)
3+
Space : O(N)
4+
5+
https://github.com/kazukiii/leetcode/pull/16/files#diff-f400dc0fa41a78f5b2a9ec5c2d5364dd8cf0da8dc9e8c39d15f52f98ea05394b
6+
を参考にした解法。
7+
平衡木(mapのkey)を利用して文字の初出インデックスを管理。
8+
二度目の出現でインデックスを削除することで、平衡木の先頭には一度しか出現していない文字のインデックスが残る。
9+
3度以上の出現で削除した要素が復帰してしまうが、インデックスが大きいため結果に影響しない。
10+
*/
11+
class Solution {
12+
public:
13+
int firstUniqChar(string s) {
14+
map<char, int> character_to_first_index;
15+
map<int, char> index_to_character;
16+
for (int i = 0; i < s.size(); i++) {
17+
char c = s[i];
18+
if (character_to_first_index.contains(c)) {
19+
int first_index = character_to_first_index[c];
20+
index_to_character.erase(first_index);
21+
continue;
22+
}
23+
character_to_first_index[c] = i;
24+
index_to_character[i] = c;
25+
}
26+
if (index_to_character.empty()) {
27+
const int kNotFound = -1;
28+
return kNotFound;
29+
}
30+
return index_to_character.begin()->first;
31+
}
32+
};

387/step2_2_suggested.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
https://github.com/colorbox/leetcode/pull/29#discussion_r1861430039
3+
を見てそれを参考に自分で書いた解法、比較用
4+
*/
5+
class Solution {
6+
public:
7+
int firstUniqChar(string s) {
8+
queue<CharacterAndCount> queue;
9+
map<char, int> character_to_count;
10+
for (int i = 0; i < s.size(); i++) {
11+
char c = s[i];
12+
character_to_count[c]++;
13+
queue.push({c, i});
14+
while (true) {
15+
char front_character = queue.front().character;
16+
if (character_to_count[front_character] > 1) {
17+
queue.pop();
18+
} else {
19+
break;
20+
}
21+
}
22+
}
23+
if (queue.empty()) {
24+
return -1;
25+
}
26+
return queue.front().index;
27+
}
28+
29+
private:
30+
struct CharacterAndCount {
31+
char character;
32+
int index;
33+
};
34+
};

387/step2_2_suggested_fixed.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
step2_2_suggested.cpp
3+
4+
https://github.com/colorbox/leetcode/pull/29#discussion_r1861430039
5+
の書き方に沿って修正したコード、参照用
6+
*/
7+
class Solution {
8+
public:
9+
int firstUniqChar(string s) {
10+
queue<CharacterAndCount> characters;
11+
map<char, int> character_to_count;
12+
for (int i = 0; i < s.size(); ++i) {
13+
char c = s[i];
14+
++character_to_count[c];
15+
characters.push({c, i});
16+
while (character_to_count[characters.front().character] >= 2) {
17+
characters.pop();
18+
}
19+
}
20+
if (characters.empty()) {
21+
return -1;
22+
}
23+
return characters.front().index;
24+
}
25+
26+
private:
27+
struct CharacterAndCount {
28+
char character;
29+
int index;
30+
};
31+
};

387/step2_3.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
Time : O(N log N)
3+
Space : O(N)
4+
5+
https://github.com/kazukiii/leetcode/pull/16/files#diff-f5b3276a9df261ed1c1e70d5b6de00ca4544e1a1ae1de6a466add8df5cd3d1b0
6+
を参考にした解法。
7+
ソートで文字種ごとに隣接させ、二度以上出現したものを除外し、min比較で最小インデックスを求める。
8+
*/
9+
class Solution {
10+
public:
11+
int firstUniqChar(string s) {
12+
vector<CharacterIndex> character_indices;
13+
for (int i = 0; i < s.size(); i++) {
14+
character_indices.push_back({s[i], i});
15+
}
16+
sort(character_indices.begin(), character_indices.end());
17+
int count = 1;
18+
int first_unique_character_index = numeric_limits<int>::max();
19+
for (int i = 0; i < s.size(); i++) {
20+
if (i < s.size() - 1 && character_indices[i].character == character_indices[i + 1].character) {
21+
count++;
22+
continue;
23+
}
24+
if (count == 1) {
25+
first_unique_character_index = min(first_unique_character_index, character_indices[i].index);
26+
}
27+
count = 1;
28+
}
29+
if (first_unique_character_index == numeric_limits<int>::max()) {
30+
return -1;
31+
}
32+
return first_unique_character_index;
33+
}
34+
35+
struct CharacterIndex {
36+
char character;
37+
int index;
38+
39+
bool operator<(const CharacterIndex& other) {
40+
return character < other.character;
41+
}
42+
};
43+
};

387/step2_4.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
Time : O(N)
3+
Space : O(1)
4+
5+
LRUを応用してできるとのことだったので実装。
6+
https://www.geeksforgeeks.org/lru-cache-implementation-using-double-linked-lists/
7+
本質的に平衡木による解法と大差ない気がする。
8+
*/
9+
struct Node {
10+
int index;
11+
int count;
12+
Node* prev;
13+
Node* next;
14+
15+
explicit Node(int i) {
16+
index = i;
17+
count = 1;
18+
prev = nullptr;
19+
next = nullptr;
20+
}
21+
};
22+
23+
class LRUCache {
24+
public:
25+
Node* head;
26+
Node* tail;
27+
map<char, Node*> character_to_node;
28+
29+
LRUCache() {
30+
head = new Node(numeric_limits<int>::max());
31+
tail = new Node(numeric_limits<int>::max());
32+
head->next = tail;
33+
tail->prev = head;
34+
}
35+
36+
~LRUCache() {
37+
for (auto [_c, node] : character_to_node) {
38+
delete node;
39+
}
40+
delete head;
41+
delete tail;
42+
}
43+
44+
void Put(char character, int index) {
45+
if (character_to_node.contains(character)) {
46+
Node *node = character_to_node[character];
47+
Remove(node);
48+
node->count++;
49+
return;
50+
}
51+
52+
Node *node = new Node(index);
53+
character_to_node[character] = node;
54+
PushBack(node);
55+
}
56+
57+
bool Empty() {
58+
return head->next->index == tail->index;
59+
}
60+
61+
private:
62+
void Remove(Node* node) {
63+
if (node->prev == nullptr) {
64+
return;
65+
}
66+
Node* prev = node->prev;
67+
Node* next = node->next;
68+
prev->next = next;
69+
next->prev = prev;
70+
node->prev = nullptr;
71+
node->next = nullptr;
72+
}
73+
74+
void PushBack(Node* node) {
75+
Node* prev = tail->prev;
76+
prev->next = node;
77+
node->prev = prev;
78+
node->next = tail;
79+
tail->prev = node;
80+
}
81+
};
82+
83+
class Solution {
84+
public:
85+
int firstUniqChar(string s) {
86+
LRUCache cache = LRUCache();
87+
for (int i = 0; i < s.size(); i++) {
88+
cache.Put(s[i], i);
89+
}
90+
if (cache.Empty() || cache.head->next->count > 1) {
91+
return -1;
92+
}
93+
return cache.head->next->index;
94+
}
95+
};
96+

387/step3.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class Solution {
2+
public:
3+
int firstUniqChar(string s) {
4+
map<char, int> character_to_count;
5+
for (const char& c : s) {
6+
character_to_count[c]++;
7+
}
8+
for (int i = 0; i < s.size(); i++) {
9+
if (character_to_count[s[i]] == 1) {
10+
return i;
11+
}
12+
}
13+
return -1;
14+
}
15+
};

0 commit comments

Comments
 (0)