请考虑以下词语:
PINEAPPLE
BANANA
ARTICHOKE
TOMATO
目标是对它进行排序(按字典顺序排列),而不是移动单词本身,而是使用字母替换。在这个例子中,我可以用A代替字母P,用P代替A,所以:
AINEPAALE
BPNPNP
PRTICHOKE
TOMPTO
这是按字典顺序排列的列表。如果你切换字母,字母将被切换为所有单词。值得注意的是,您可以使用整个字母表,仅使用列表中单词中的字母。
我花了相当多的时间来解决这个问题,但是除了暴力强迫它(尝试所有的字母开关组合)之外我还没有想到任何其他东西,也没有能够在列表可以时提出定义的条件分类。
更多例子:
ABC
ABB
ABD
可以变成
ACB
ACC
ACD
满足条件。
答案 0 :(得分:4)
让我们假设特定情况下的问题是可能的,就在现在。此外,为简单起见,假设所有单词都是不同的(如果两个单词相同,则它们必须相邻,一个可以忽略)。
然后问题转变为拓扑排序,尽管细节与可疑狗的答案略有不同,后者错过了几个案例。
考虑26个节点的图表,标记为A
到Z
。每对单词对部分排序贡献一个有向边;这对应于单词不同的第一个字符。例如,按顺序使用两个单词ABCEF
和ABRKS
,第一个区别在于第三个字符,因此sigma(C) < sigma(R)
。
可以通过对此图进行拓扑排序,并将A
替换为排序中的第一个节点,B
替换第二个节点等来获得结果。
请注意,这也可以衡量问题何时无法解决。当两个单词相同但不相邻时(在#34;簇&#34;中),当一个单词是另一个单词的前缀但是在它之后,或者当图形具有循环并且拓扑排序是不可能的时候,则会发生这种情况。
这是Python中一个功能齐全的解决方案,可以检测问题的特定实例何时无法解析。
def topoSort(N, adj):
stack = []
visited = [False for _ in range(N)]
current = [False for _ in range(N)]
def dfs(v):
if current[v]: return False # there's a cycle!
if visited[v]: return True
visited[v] = current[v] = True
for x in adj[v]:
if not dfs(x):
return False
current[v] = False
stack.append(v)
return True
for i in range(N):
if not visited[i]:
if not dfs(i):
return None
return list(reversed(stack))
def solve(wordlist):
N = 26
adj = [set([]) for _ in range(N)] # adjacency list
for w1, w2 in zip(wordlist[:-1], wordlist[1:]):
idx = 0
while idx < len(w1) and idx < len(w2):
if w1[idx] != w2[idx]: break
idx += 1
else:
# no differences found between the words
if len(w1) > len(w2):
return None
continue
c1, c2 = w1[idx], w2[idx]
# we want c1 < c2 after the substitution
adj[ord(c1) - ord('A')].add(ord(c2) - ord('A'))
li = topoSort(N, adj)
sub = {}
for i in range(N):
sub[chr(ord('A') + li[i])] = chr(ord('A') + i)
return sub
def main():
words = ['PINEAPPLE', 'BANANA', 'ARTICHOKE', 'TOMATO']
print('Before: ' + ' '.join(words))
sub = solve(words)
nwords = [''.join(sub[c] for c in w) for w in words]
print('After : ' + ' '.join(nwords))
if __name__ == '__main__':
main()
编辑:此解决方案的时间复杂度是可证明最佳的O(S)
,其中S
是输入的长度。感谢suspicious dog为此;原始时间复杂度为O(N^2 L)
。
答案 1 :(得分:1)
更新:正如Eric Zhang所指出的那样,原始分析是错误的并且在某些测试用例类上失败了。
我相信这可以通过topological sort的形式解决。您的初始单词列表定义了某组字母的部分顺序或有向图。您希望找到使该字母图线性化的替换。让我们使用您的一个非平凡的例子:
P A R K O V I S T E
P A R A D O N T O Z A
P A D A K
A B B A
A B E C E D A
A B S I N T
让x <* y
表示某些字母(或单词)substitution(x) < substitution(y)
和x
y
。我们总体上需要word1 <* word2 <* word3 <* word4 <* word5 <* word6
,但就字母而言,我们只需要查看每对相邻的单词,并在同一列中找到第一对不同的字符:
K <* A (from PAR[K]OVISTE <* PAR[A]DONTOZA)
R <* D (from PA[R]ADONTOZA <* PA[D]AK)
P <* A (from [P]ADAK <* [A]BBA)
B <* E (from AB[B]A <* AB[E]CEDA)
E <* S (from AB[E]CEDA <* AB[S]INT)
如果我们发现没有不匹配的字母,则有3种情况:
在案例1和案例2中,单词已经按字典顺序排列,因此我们不需要执行任何替换(尽管我们可能),并且它们不会添加我们需要遵守的额外约束。在案例3中,没有任何替代可以解决这个问题(想想["DOGGO", "DOG"]
),因此没有可能的解决方案,我们可以提前退出。
否则,我们构建与我们获得的部分排序信息相对应的有向图并执行拓扑排序。如果排序过程指示不可能进行线性化,则没有用于对单词列表进行排序的解决方案。否则,你会得到类似的东西:
P <* K <* R <* B <* E <* A <* D <* S
根据您实现拓扑排序的方式,您可能会获得不同的线性排序。现在,您只需要为每个字母分配一个符合此排序的替换,并按字母顺序排序。一个简单的选择是将线性排序与自己按字母顺序排序,并将其用作替换:
P <* K <* R <* B <* E <* A <* D <* S
| | | | | | | |
A < B < D < E < K < P < R < S
但如果您愿意,可以实施不同的替换规则。
这是Python中的概念验证:
import collections
import itertools
# a pair of outgoing and incoming edges
Edges = collections.namedtuple('Edges', 'outgoing incoming')
# a mapping from nodes to edges
Graph = lambda: collections.defaultdict(lambda: Edges(set(), set()))
def substitution_sort(words):
graph = build_graph(words)
if graph is None:
return None
ordering = toposort(graph)
if ordering is None:
return None
# create a substitition that respects `ordering`
substitutions = dict(zip(ordering, sorted(ordering)))
# apply substititions
return [
''.join(substitutions.get(char, char) for char in word)
for word in words
]
def build_graph(words):
graph = Graph()
# loop over every pair of adjacent words and find the first
# pair of corresponding characters where they differ
for word1, word2 in zip(words, words[1:]):
for char1, char2 in zip(word1, word2):
if char1 != char2:
break
else: # no differing characters found...
if len(word1) > len(word2):
# ...but word2 is a prefix of word1 and comes after;
# therefore, no solution is possible
return None
else:
# ...so no new information to add to the graph
continue
# add edge from char1 -> char2 to the graph
graph[char1].outgoing.add(char2)
graph[char2].incoming.add(char1)
return graph
def toposort(graph):
"Kahn's algorithm; returns None if graph contains a cycle"
result = []
working_set = {node for node, edges in graph.items() if not edges.incoming}
while working_set:
node = working_set.pop()
result.append(node)
outgoing = graph[node].outgoing
while outgoing:
neighbour = outgoing.pop()
neighbour_incoming = graph[neighbour].incoming
neighbour_incoming.remove(node)
if not neighbour_incoming:
working_set.add(neighbour)
if any(edges.incoming or edges.outgoing for edges in graph.values()):
return None
else:
return result
def print_all(items):
for item in items:
print(item)
print()
def test():
test_cases = [
('PINEAPPLE BANANA ARTICHOKE TOMATO', True),
('ABC ABB ABD', True),
('AB AA AB', False),
('PARKOVISTE PARADONTOZA PADAK ABBA ABECEDA ABSINT', True),
('AA AB CA', True),
('DOG DOGGO DOG DIG BAT BAD', False),
('DOG DOG DOGGO DIG BIG BAD', True),
]
for words, is_sortable in test_cases:
words = words.split()
print_all(words)
subbed = substitution_sort(words)
if subbed is not None:
assert subbed == sorted(subbed), subbed
print_all(subbed)
else:
print('<no solution>')
print()
print('expected solution?', 'yes' if is_sortable else 'no')
print()
if __name__ == '__main__':
test()
现在,它并不理想 - 例如,即使原始的单词列表已经排序,它仍会执行替换 - 但它似乎有效。我无法正式证明它有效,所以如果你找到反例,请告诉我!
答案 2 :(得分:0)
用所有单词替换P( P ineapple)。
用B代替所有单词中的B.
用P。
替换所有单词中的A.用T替换所有单词中的T.
这将为您提供预期的结果。
编辑:
示例 -
ABC&lt; ABB
第一次出现字符不匹配的是第3位。所以我们将所有的C与B&C进行交换。