Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions extend_exp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
a = [1, 2, 3]
b = [4, 5, 6]
a.extend(b)
print(a)
b.append(7)
print(a)

# [1, 2, 3, 4, 5, 6]
# [1, 2, 3, 4, 5, 6]

a = [[1], [2], [3]]
b = [[4], [5], [6]]
a.extend(b)
print(a)
b[0].append(7)
print(a)

# [[1], [2], [3], [4], [5], [6]]
# [[1], [2], [3], [4, 7], [5], [6]]
646 changes: 646 additions & 0 deletions memo.md

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions step1-1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from typing import List
from collections import deque
import copy


class Node:
def __init__(self, id : int, nexts : List[Node]):
self.id = id
self.nexts = nexts

def __repr__(self) :
return f"Node(id={self.id})"

class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:

def is_one_word_difference(word1, word2):
if len(word1) != len(word2):
raise ValueError(f"The length of {word1} and {word2} are different")
difference_count = 0
word_len = len(word1)
for i in range(word_len):
if word1[i] == word2[i]:
continue
difference_count += 1
return difference_count == 1

start_node_id = None
end_node_id = None
id_to_node = {}
for word1_id in range(len(wordList)):
if wordList[word1_id] == beginWord:
start_node_id = word1_id
elif wordList[word1_id] == endWord:
end_node_id = word1_id

if word1_id not in id_to_node:
id_to_node[word1_id] = Node(word1_id, [])
word1_node = id_to_node[word1_id]
for word2_id in range(word1_id + 1, len(wordList)):
if not is_one_word_difference(wordList[word1_id], wordList[word2_id]):
continue
if word2_id not in id_to_node:
id_to_node[word2_id] = Node(word2_id, [])
word2_node = id_to_node[word2_id]
word1_node.nexts.append(word2_node)
word2_node.nexts.append(word1_node)


copy_word_list = copy.deepcopy(wordList)
if start_node_id is None:
copy_word_list.extend([beginWord])
start_node_id = len(copy_word_list) - 1
start_node = Node(start_node_id, [])
id_to_node[start_node_id] = start_node
for word_id in range(len(wordList)):
if not is_one_word_difference(beginWord, copy_word_list[word_id]):
continue
start_node.nexts.append(id_to_node[word_id])
id_to_node[word_id].nexts.append(start_node)

total_words = len(copy_word_list)

visited = [False] * total_words
candidate_nodes = deque()
candidate_nodes.append(id_to_node[start_node_id])

distance = 0
while candidate_nodes:
num_nodes = len(candidate_nodes)
distance += 1
for _ in range(num_nodes):
node = candidate_nodes.popleft()
if node.id == end_node_id:
return distance
if visited[node.id]:
continue
visited[node.id] = True
for connected_node in node.nexts:
if visited[connected_node.id]:
continue
candidate_nodes.append(connected_node)

NOT_FOUND = 0
return NOT_FOUND
47 changes: 47 additions & 0 deletions step2-1_alphabet_replacement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from collections import deque, defaultdict
import copy

class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:

def construct_word_to_adjacents_dict(word_list):
word_to_adjacents = defaultdict(set)
word_set = set(word_list)
for word in word_list:
for i in range(len(word)):
for alphabet_ord in range(ord("a"), ord("z") + 1):
alphabet = chr(alphabet_ord)
if word[i] == alphabet:
continue
ith_replaced = f"{word[:i]}{alphabet}{word[i + 1:]}"
if ith_replaced in word_set:
word_to_adjacents[word].add(ith_replaced)
return word_to_adjacents

copy_word_list = copy.deepcopy(wordList)
if beginWord not in copy_word_list:
copy_word_list.append(beginWord)

word_to_adjacents = construct_word_to_adjacents_dict(copy_word_list)

visited = {word : False for word in copy_word_list}
candidate_queue = deque()
candidate_queue.append(beginWord)
distance = 0
while candidate_queue:
num_candidates = len(candidate_queue)
distance += 1
for _ in range(num_candidates):
cur_word = candidate_queue.popleft()
if visited[cur_word]:
continue
visited[cur_word] = True
if cur_word == endWord:
return distance
if cur_word not in word_to_adjacents:
continue
for adj_word in word_to_adjacents[cur_word]:
candidate_queue.append(adj_word)

NOT_FOUND = 0
return NOT_FOUND
50 changes: 50 additions & 0 deletions step2-2_wild_card.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from collections import deque, defaultdict
import copy

class Solution:
def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:

def construct_word_to_adjacents_dict(word_list):
# MEMO: 実行時にforのwordが取り込まれそうで怖いからwに変数名を変えた
get_patterns = lambda w : [ (w[:i], w[i + 1:]) for i in range(len(w))]
pattern_to_words = defaultdict(list)
for word in word_list:
for pattern in get_patterns(word):
pattern_to_words[pattern].append(word)

word_to_adjacents = defaultdict(list)
for word in word_list:
for pattern in get_patterns(word):
adj_words = pattern_to_words[pattern]
# MEMO: extendの時に後追加されるものはdeepcopyなのか???今回はshallowでも影響ないけど
word_to_adjacents[word].extend(adj_words)

return word_to_adjacents

word_list_copy = copy.deepcopy(wordList)
if beginWord not in word_list_copy:
word_list_copy.append(beginWord)

word_to_adjacents = construct_word_to_adjacents_dict(word_list_copy)

visited = set()
candidates = deque()
candidates.append(beginWord)
distance = 0
while candidates:
num_candidates = len(candidates)
distance += 1
for _ in range(num_candidates):
cur_word = candidates.popleft()
if cur_word in visited:
continue
visited.add(cur_word)
if cur_word == endWord:
return distance
for adj_word in word_to_adjacents[cur_word]:
if adj_word in visited:
continue
candidates.append(adj_word)

NOT_FOUND = 0
return NOT_FOUND
66 changes: 66 additions & 0 deletions step2-3_former_latter_dict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from collections import deque, defaultdict
import copy

class Solution:
def get_word_to_adjacents(self, word_list: list[str]) -> dict[str, list[str]]:
word_to_adjacents = defaultdict(list)

def register_to_word_to_adjacents_in_range(start_idx, end_idx, candidates):
if start_idx > end_idx:
return
if start_idx == end_idx:
for i in range(len(candidates)):
for j in range(len(candidates)):
if i == j:
continue
word_to_adjacents[candidates[i]].append(candidates[j])
return

mid_idx = (start_idx + end_idx) // 2
former_to_matched_words = defaultdict(list)
latter_to_matched_words = defaultdict(list)
for candidate in candidates:
former = candidate[start_idx:mid_idx + 1]
if former:
former_to_matched_words[former].append(candidate)
latter = candidate[mid_idx + 1:end_idx + 1]
if latter:
latter_to_matched_words[latter].append(candidate)

for former_matched_candidates in former_to_matched_words.values():
register_to_word_to_adjacents_in_range(mid_idx + 1, end_idx, former_matched_candidates)
for latter_matched_candidates in latter_to_matched_words.values():
register_to_word_to_adjacents_in_range(start_idx, mid_idx, latter_matched_candidates)
return

register_to_word_to_adjacents_in_range(0, len(word_list[0]) - 1, word_list)
return word_to_adjacents


def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
copy_word_list = copy.deepcopy(wordList)
if beginWord not in copy_word_list:
copy_word_list.append(beginWord)

word_to_adjacents = self.get_word_to_adjacents(copy_word_list)

visited = set()
candidates = deque()
candidates.append(beginWord)
distance = 0
while candidates:
num_candidates = len(candidates)
distance += 1
for _ in range(num_candidates):
cur_word = candidates.popleft()
if cur_word in visited:
continue
visited.add(cur_word)
if cur_word == endWord:
return distance
for adj_word in word_to_adjacents[cur_word]:
if adj_word in visited:
continue
candidates.append(adj_word)
NOT_FOUND = 0
return NOT_FOUND
55 changes: 55 additions & 0 deletions step3-1_alphabet_replacement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import copy
from typing import Generator
from collections import defaultdict, deque


class Solution:
def get_word_to_adjacents_dict(self, word_list: list[str]) -> dict[str, list[str]]:

def yield_one_alphabet_replaced(word: str) -> Generator[str, None, None]:
for pos in range(len(word)):
for alphabet_ord in range(ord("a"), ord("z") + 1):
alphabet = chr(alphabet_ord)
if word[pos] == alphabet:
continue
yield f"{word[:pos]}{alphabet}{word[pos + 1:]}"

word_to_adjacents = defaultdict(list)

word_list_set = set(word_list)
for word in word_list:
for replaced_word in yield_one_alphabet_replaced(word):
if replaced_word in word_list_set:
word_to_adjacents[word].append(replaced_word)

return word_to_adjacents


def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
word_list_copy = copy.deepcopy(wordList)
if beginWord not in word_list_copy:
word_list_copy.append(beginWord)

word_to_adjacents = self.get_word_to_adjacents_dict(word_list_copy)

candidates = deque()
candidates.append(beginWord)
visited = set()
distance = 0
while candidates:
distance += 1
num_cur_candidates = len(candidates)
for _ in range(num_cur_candidates):
word = candidates.popleft()
if word == endWord:
return distance
if word in visited:
continue
visited.add(word)
for adj_word in word_to_adjacents[word]:
if adj_word not in visited:
candidates.append(adj_word)

NOT_FOUND = 0
return NOT_FOUND

54 changes: 54 additions & 0 deletions step3-2_wild_card.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from typing import Generator
from collections import defaultdict, deque
import copy


class Solution:
def get_word_to_adjacents_dict(self, word_list: list[str]) -> dict[str, list[set]]:

def yield_pattern(word: str) -> Generator[tuple[str, str], None, None]:
for i in range(len(word)):
yield (word[:i], word[i + 1:])

pattern_to_words = defaultdict(set)
for word in word_list:
for pattern in yield_pattern(word):
pattern_to_words[pattern].add(word)

word_to_adjacents = defaultdict(set)
for word in word_list:
for pattern in yield_pattern(word):
word_to_adjacents[word] = word_to_adjacents[word] | pattern_to_words[pattern]

return word_to_adjacents



def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int:
word_list_copy = copy.deepcopy(wordList)
if beginWord not in word_list_copy:
word_list_copy.append(beginWord)

word_to_adjacents = self.get_word_to_adjacents_dict(word_list_copy)

visited = set()
candidates = deque()
candidates.append(beginWord)
distance = 0
while candidates:
num_cur_candidates = len(candidates)
distance += 1
for _ in range(num_cur_candidates):
word = candidates.popleft()
if word == endWord:
return distance
if word in visited:
continue
visited.add(word)
for adj_word in word_to_adjacents[word]:
if adj_word in visited:
continue
candidates.append(adj_word)

NOT_FOUND = 0
return NOT_FOUND
Loading