从单词列表中找到最短的字母


10

一个全字母短句是一个包含每个字母串a- z英文字母,不区分大小写的。(如果pangram包含一个字母的多个副本,或者除字母之外还包含非字母字符,则可以。)

编写一个程序或函数,其输入为字符串列表,并输出一个或多个具有以下属性的字符串:

  • 每个输出字符串必须是一个字符集。
  • 每个输出字符串必须通过串联输入列表中的一个或多个字符串来形成,并用空格分隔。
  • 在具有这些属性的所有字符串中,每个输出字符串必须最短,或者最短。

许多程序将选择仅输出一个字符串;您只需要输出多个字符串,否则就不得不编写额外的代码来限制输出。

您可以假定输入中没有不可打印的字符或空格,并且输入的单词长度不超过(列表长度的自然对数的26倍)个字符。(但是,您可能不会假设输入内容只包含字母,或者仅包含小写字母;标点符号和大写字母完全可能。)

输入和输出可以任何合理的格式给出。为了测试您的程序,我建议使用两个测试用例:英语单词词典(大多数计算机都有一个),以及以下情况(不可能有完美的字母(26个字母)),因此您必须找到一个包含重复的字母):

abcdefghi
defghijkl
ijklmnop
lmnopqrs
opqrstuvw
rstuvwxyz

您应随程序一起提供程序输出的样本。(由于使用了不同的单词列表,这对于不同的人可能会有所不同。)

胜利条件

这是一个 挑战。优胜者是在多项式时间内运行的最短程序(以字节为单位)。(针对不知道这意味着什么的人的摘要:如果将单词列表的大小加倍,则程序的速度应不超过一个恒定因子。但是,有问题的恒定因子可能与您一样大例如,将它变慢4倍或8倍是有效的,但是变小它不是单词列表长度的一个因素;它变慢的因素必须加以限制。)


在确定复杂度时,我们可以利用每个单词最多26个字母的事实吗?字母大小是26的常数吗?
xnor

是。我对该输入进行了部分限制,以使复杂度更易于定义/计算。

我认为这涉及到技术性。如果忽略重复的输入单词,则最多有27 ^ 26个可能的输入单词,因此最多有2 ^(27 ^ 26)个可能的子集作为可能的输入。这是巨大的,但却是一个常数。因此,此有限集上的任何程序都是恒定时间,该常数是在所有可能的输入上采取的最大步数。
xnor

我没有说输入中没有重复的单词。我猜您可以通过过滤掉标点符号并首先对输入进行重复数据删除而在“技术上”的O(n)时间内运行该程序,(或者更可能是O(n log n),这将使用比基数少的内存)重复数据删除将)。然后,您必须从过滤的版本返回到原始单词列表。但是,除非您实际上经过了所有这些步骤,否则您无法声明所涉及的多项式时间!

我忘记了非字母。我们是否可以假设它们是ASCII或其他有限集内的?如果是这样,我认为任何以重复数据删除开始的算法都可以声称是多项式时间。
xnor

Answers:


3

Ruby 159(迭代)

红宝石 227 220 229 227 221(递归)

新的迭代解决方案(基于@Niel描述的算法):

c={('A'..'Z').to_a=>""}
while l=gets
d=c.clone
c.map{|k,v|j=k-l.upcase.chars
w=v+" "+l.strip
d[j]=w if !c[j]||c[j].size<w.size}
c=d
end
x=c[[]]
p x[1..-1] if x

旧的递归解决方案:

W=[]
while l=gets
W<<l.strip
end
I=W.join(" ")+"!!"
C={[]=>""}
def o(r)if C[r]
C[r]
else
b=I
W.map{|x|s=r-x.upcase.chars
if s!=r
c=x+" "+o(s)
b=c if c.size<b.size
end}
C[r]=b
end
end
r=o ('A'..'Z').to_a
p r[0..-2] if r!=I

字节测量基于忽略文件中的最后换行符,与无关紧要ruby 2.3.1p112。修复了一个小错误(添加后,字节数又增加了).downcase .upcase 针对问题陈述所要求的不区分大小写)。

这是缩短标识符等之前的早期版本:

#!/usr/bin/env ruby

$words = [];

while (line=gets)
  $words << line[0..-2];
end

$impossible = $words.join(" ")+"!!";

$cache = {};

def optimize(remaining)
  return $cache[remaining] if ($cache[remaining]);
  return "" if (remaining == []);

  best = $impossible;

  $words.each{|word|
    remaining2 = remaining - word.chars;
    if (remaining2 != remaining)
      curr = word + " " + optimize(remaining2);
      best = curr if (curr.length < best.length);
    end
  };

  $stderr.puts("optimize(#{remaining.inspect})=#{best.inspect}");

  return $cache[remaining] = best;
end

result = optimize(('a'..'z').to_a);

puts(result[0..-1]);

它是如何工作的?基本上,它保留了一组仍要覆盖的字符,并且只有在减少一个未覆盖的字符集的情况下,它才会重复出现。此外,递归的结果被记忆。2 ^ 26的每个子集对应一个备注表条目。每个此类条目的时间计算均与输入文件的大小成比例。所以整个事情是O(N)N输入文件的大小在哪里),尽管有一个很大的常数。


1

的JavaScript(ES6),249个 248字节,可能竞争

a=>a.map(w=>w.replace(/[a-z]/gi,c=>b|=1<<parseInt(c,36)-9,b=0,l=w.length)&&(m.get(b)||[])[0]<l||m.set(b,[l,w]),m=new Map)&&[...m].map(([b,[l,w]])=>m.forEach(([t,s],e)=>(m.get(e|=b)||[])[0]<=t+l||m.set(e,[t+l+1,s+' '+w])))&&(m.get(-2^-1<<27)||[])[1]

说明:通过将字母转换为位掩码来转换数组,只为映射中的每个位掩码保存最短的单词。然后遍历地图的副本,如果结果字符串较短,则通过添加每个组合的位掩码来扩充地图。最后返回保存给Pangram的位图的字符串。(undefined如果不存在这样的字符串,则返回。)


有趣。您能否详细说明它的工作方式,如果可以的话,发布未发布的代码?
DepressedDaniel

1
这应该是一个有效/竞争的条目。我认为这实际上在O(n log n)中运行!(地图具有2²limit的硬限制,因此不会显示复杂性;因此花费的唯一时间就是读取输入的时间。)

我只是重新阅读了说明,现在就知道它是如何工作的。整齐。+1 ...嗯,它什么时候决定停止考虑通过成对增加地图?它应该继续下去,直到无法放松为止。
DepressedDaniel

@DepressedDaniel对于从原始单词列表中提取的每个位掩码,它会检查到目前为止找到的所有部分Pangram,以及是否添加该单词会创建一个短于当前组合位掩码的Pangram。
尼尔,2016年

@ ais523对于大输入(> 1000个单词),大多数时间似乎都花在交换上。我尝试从Map切换到Array,但速度变慢了!
尼尔

-1

Python 3中,9894,92个字节

print([s for s in input().split()if sum([1 for c in range(65,91)if chr(c)in s.upper()])>25])

遍历字母的ASCII表示形式,如果在字符串中找到字母,则在列表中添加1。如果列表的总和大于25,则包含所有字母并将被打印。


我认为您可以在(' ')和之间删除空格if。您也可以更改ord(i) in range(65,91)91>x>=65。还有,复杂度是多少?
NoOneIsHere

1
该解决方案的复杂性是什么?它需要的答案是在多项式复杂性,否则是非竞争性的。
NoOneIsHere

抱歉,我认为是O(n),因为输入列表的长度可能有所不同,但是
Erich

抱歉,我认为它是O(n),因为输入列表的长度可以变化,但是第二个循环总是从65到90。但是我尚未对其进行测试。
埃里希

不确定是否满足以下条件:“在具有这些属性的所有字符串中,每个输出字符串必须最短,或捆绑在一起。”
DepressedDaniel
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.