给定元素周期表中的元素名称,找到可以组成的最长单词?
需要将像
Na
,Ne
这样的符号视为单个元素。这是在一家知名公司的面试中提出的问题。请问有谁能帮我解决这个问题?
Na
,Ne
这样的符号视为单个元素。Words= ['K', 'NaH', 'NaNaNaNa', 'HaKuNa']
Symbols= ['Na','K','H']
def Match(Word):
# Match the end of the word with every symbol
for S in Symbols:
# In case of a partial match, recurse on the prefix
if S == Word or (S == Word[-len(S):] and Match(Word[:-len(S)])):
print S,
return True
# No match, report failure
return False
for W in Words:
if Match(W):
print
>>>
K
Na H
Na Na Na Na
elements = "H He Li Be B C N O F Ne Na Mg Al Si P S Cl Ar K Ca Sc Ti V Cr Mn Fe Co Ni Cu Zn Ga Ge As Se Br Kr Rb Sr Y Zr Nb Mo Tc Ru Rh Pd Ag Cd In Sn Sb Te I Xe Cs Ba La Ce Pr Nd Pm Sm Eu Gd Tb Dy Ho Er Tm Yb Lu Hf Ta W Re Os Ir Pt Au Hg Tl Pb Bi Po At Rn Fr Ra Ac Th Pa U Np Pu Am Cm Bk Cf Es Fm Md No Lr Rf Db Sg Bh Hs Mt Ds Rg Cn Uut Fl Uup Lv Uus Uuo".split()
def decompose(word):
"""Express given word as chemical compound. If there are multiple solutions, return one of minimal weight."""
progress = [False for x in range(len(word)+1)] # solution for word[:i]
progress[0] = []
for i in range(1, len(word)+1):
possibles = list()
for j in range(max(i-3,0), i):
if progress[j] == False:
continue
alchemical = word[j:i].title()
if alchemical in elements:
possibles.append(progress[j] + [alchemical])
if possibles:
# choose minimal solution
progress[i] = min(possibles, key=len)
if progress[-1] == False:
return False
return "".join(progress[-1])
assert decompose("sine") == "SiNe" # rather than S-I-Ne
assert decompose("bismuth") == "BiSmUTh"
assert decompose("jam") == False
https://gist.github.com/hickford/750135db633832913703
在整个词典上运行此程序,最长的复合词是:
NoNRePReSeNTaTiONaLiSm
ThErMoPHOsPHoReSCeNCe
BrONCHoEsOPHAgOsCoPY
HYPErPHOsPHoReSCeNCe
HYPSiBrAcHYCePHAlISm
我只是想记录下我的想法,并与其他专业的算法人员进行验证,因为我在其他地方没有看到这样的问题。
Backtracking void(String result, String[] PeriTable, String[] used){
for(int i=0;i<PeriTable.length;i++){
If(!used.contain(PeriTable[i])){
newResult=result+PeriTable[i];
If(isEnglishWord(newResult)) {
used.add(PeriTable[i]);
maxlength=Math.max(maxlength,newResult.length());
Backtracking(newResult, PeriTable, used);
used.remove(used.length()-1);
}
}
else continue;
}
}
然而,我不知道如何构建isEnglishWord(); 函数。
基于生成所有长度的排列:
import itertools..
symbols = ['Na','K','H']
for i in range(len(symbols)):
for word in itertools.permutations(symbols,i+1):
print( ''.join(word) )
您可以生成每种可能的组合,并检查它是否是实际单词。但这是低效的,仅适用于不允许符号重复的情况。
如果允许重复,则需要将单词与符号列表进行比较。我建议采用以下方法:
import itertools..
words = ['K', 'NaH', 'NaNaNaNa', 'HaKuNa']
symbols = ['Na','K','H']
for i in range(len(symbols)):
for word in itertools.permutations(symbols,i+1):
print( ''.join(word) )
def can_be_built(word):
pos = 0
ret = True
while(pos < len(word)):
#following loop checks if word starting form `pos`
#can be prefixed by a symbol
symbol_match = False
for symbol in symbols:
if word[pos:pos+len(symbol)] == symbol:
symbol_match = True
break
if symbol_match == False:
print('Word `{}` has no match for symbol from: {}'.format(word, word[pos:]))
ret = False
break
#if it does move forward
pos += len(symbol)
return ret
for word in words:
print("{} can be built? {}".format(word, can_be_built(word)))
它迭代地检查一个单词前缀是否为符号,然后向前移动直到单词的末尾被到达。
输出:
K can be built? True
NaH can be built? True
NaNaNaNa can be built? True
Word `HaKuNa` has no match for symbol from: aKuNa
HaKuNa can be built? False
它还不是完美的。
正如Makoto所说,前缀检查应该返回每个可能的匹配列表。算法应该将这些匹配项排入队列,并检查所有可能的路径。这有点像构建一个与单词匹配的前缀图。如果能构建整个单词,就成功了。
我认为修正我的第二个示例仍然相当容易,但是我没有时间编写实际的代码了。我认为这是一个很好的起点。
[编辑:本帖子基本上只是对Niklas B.在其他帖子评论中提到的DP算法的全面解释。]
在一个可以由化学元素名称组成的单词中,至少有以下一种情况为真:
这表明了一个很好的DP算法,对于给定长度为k的单词w,我们计算出所有1 <= i <= k的值,即w的长度-i前缀可以构建的最大字母数(或化学符号数;问题在于我们要最大化什么!)。让我们称之为f(i),如果w的长度-i前缀根本不能从化学元素名称中构建,则将f(i) = -1。
让X = 1以最大化元素名称,或者X = 2以最大化字母计数。
DP的递归可以写成:
那么f(k)就是我们想要知道的:可以从中形成的最大数量的(字母/元素),如果不可能,则为-1。
max()表示如果只有一种处理前缀结尾的方法有效,那么将选择该方法(因为另一种方法的得分为-1,总是比较小),如果两种方法都无效,我们将正确地得到f(i)=-1。
假设单个字符或字符对是否为有效化学元素名称的测试是常数时间(使用例如数组或哈希表查找),我们可以计算给定单词是否可以表示为其长度成比例的化学元素名称序列的时间和空间。
如果我们要最大化字母数量,那么按照长度递减的顺序处理字典可能是最合理的,因为第一次遇到 f(k) 不等于 -1 的单词时,我们就知道它必须是最长的,可以停止处理。
这可以用于最大化元素名称计数。虽然我们不能立即停止,因为可能会有一个更短的单词在后面,但仍然可以从中获得一些有用的信息,特别是当我们发现一个新单词的元素计数比先前的最佳值更大时:我们可以使用此元素计数更新截止阈值,并在看到比此阈值更短的单词时停止,因为长度为 m 的单词永远无法从超过 m 个元素名称中形成。
还有一种不同的方法,可能会更快:通过首先按字母顺序对字典进行排序,我们可以避免在与前一个单词共享的前缀上重新计算 f(i)。例如,如果“carton”紧随“cart”之后出现在字典中,则我们只需要在“on”部分上计算 f(i)。
很抱歉,我对这些答案感到非常困惑。显然,最简单的方法是使用嵌套桶排序将字典按字母顺序排序,直到某个深度,比如8个字母,这样可以让你以顺序1检索以给定序列字母开头的单词,最多8个字母。
然后,像玩游戏树一样遍历周期表。在每一步中,您都会添加一个元素,然后向下检查列表,看是否有以该组合开头的单词。
这将相对较耗费内存,因为您可能需要至少6层桶(按前6个字母进行字母排序),但通常只有大约100万个单词。由于游戏树中几乎每个分支都会在四个字母后终止,因此您的DFS将非常快速地搜索整个游戏树。如果您可以形成字典中的每个单词,那么仍然必须存在与单词数量相同的分支数,实际上您只能获得字典中的一小部分单词,因此这种方法必须是有效的。