它是前缀代码吗?


33

在信息论中,“前缀代码”是一个字典,其中所有键都不是另一个的前缀。换句话说,这意味着没有一个字符串以其他任何字符串开头。

例如,{"9", "55"}是前缀代码,但{"5", "9", "55"}不是。

这样做的最大优点是,可以将编码的文本记下来,并且它们之间没有分隔符,并且仍然可以唯一地解密。这在诸如Huffman编码之类的压缩算法中得到了体现,该算法始终会生成最佳的前缀代码。

您的任务很简单:给定一个字符串列表,确定它是否是有效的前缀代码。

您的输入:

  • 将是任何合理格式的字符串列表。

  • 仅包含可打印的ASCII字符串。

  • 将不包含任何空字符串。

您的输出将是一个true / falsey值:如果是有效的前缀代码,则为True,否则为falsey。

这是一些真实的测试用例:

["Hello", "World"]                      
["Code", "Golf", "Is", "Cool"]
["1", "2", "3", "4", "5"]
["This", "test", "case", "is", "true"]          

["111", "010", "000", "1101", "1010", "1000", "0111", "0010", "1011", 
 "0110", "11001", "00110", "10011", "11000", "00111", "10010"]

以下是一些错误的测试案例:

["4", "42"]                             
["1", "2", "3", "34"]                   
["This", "test", "case", "is", "false", "t"]
["He", "said", "Hello"]
["0", "00", "00001"]
["Duplicate", "Duplicate", "Keys", "Keys"]

这是代码高尔夫球,因此存在标准漏洞,并且以字节为单位的最短答案将获胜。


您想要一个一致的真实值还是它是“某个正整数”(在不同输入之间可能有所不同)。
马丁·恩德


@DrGreenEg​​gsandHamDJ我认为答案根本不是要解决输出的一致性,因此是个问题。;)
Martin Ender's

只是出于好奇:该挑战说:“这样做的最大好处是,可以写下编码的文本,而它们之间没有分隔符,并且仍然可以唯一地解密。” 什么样的东西001才能被独特地解读?可以是00, 10, 11
Joba'5

2
@Joba这取决于您的钥匙。如果将0, 00, 1, 11所有键都用作键,则这不是前缀码,因为0是前缀00,而前缀是11。前缀码是所有键都不以另一个键开头的位置。因此,例如,如果您的密钥是0, 10, 11前缀代码,并且可以唯一解密。001是不是有效的消息,但0011还是0010有独特的辨认。
DJMcMayhem

Answers:


11

Pyth,8个字节

.AxM.PQ2

测试套件

接受输入的所有2个元素置换,将每个映射到另一个字符串中另一个字符串的索引(前缀为0),然后返回所有结果是否为真(非零)。


12

Haskell,37个字节

f l=[x|x<-l,y<-l,zip x x==zip x y]==l

每个元素xl前缀都会重复一次,对于没有前缀的列表恰好会重复一次,从而给出原始列表。可以通过用zip压缩两个列表来检查prefix属性x,这会切断超出长度的元素x


这是一个优雅的解决方案(+1)
Michael Klein

9

Java中,128个 127 126 125 124 121字节

(感谢@Kenny Lau,@ Maltysen,@ Patrick Roberts,@ Joba)

Object a(String[]a){for(int i=0,j,l=a.length;i<l;i++)for(j=0;j<l;)if(i!=j&a[j++].startsWith(a[i]))return 1<0;return 1>0;}

不打高尔夫球

Object a(String[] a) {
    for (int i = 0, j, l = a.length; i < l; i++) 
        for (j = 0; j < l;) 
            if (i != j & a[j++].startsWith(a[i])) return 1<0;
    return 1>0;
}

输出量

[Hello, World]
true

[Code, Golf, Is, Cool]
true

[1, 2, 3, 4, 5]
true

[This, test, case, is, true]
true

[111, 010, 000, 1101, 1010, 1000, 0111, 0010, 1011, 0110, 11001, 00110, 10011, 11000, 00111, 10010]
true

[4, 42]
false

[1, 2, 3, 34]
false

[This, test, case, is, false, t]
false

[He, said, Hello]
false

[0, 00, 00001]
false

[Duplicate, Duplicate, Keys, Keys]
false

1
idk bout java,但是可以&代替&&
Maltysen

1
对,保存另一个字节。在Java中,将按位运算符与布尔操作数一起使用的行为与普通逻辑运算符一样,只是它们不会短路(在这种情况下不需要)。
Marv

您不能只将函数的返回类型更改为intreturn 01吗?这样可以节省几个字节。此外,我忘了,如果这是有效的Java编写,但如果你声明ijl外内for循环,将来自一个分号少救一个字节。
帕特里克·罗伯茨

@PatrickRoberts Maltysen之前曾提出过此建议,但根据对Truey / Falsey的最高支持定义,这是无效的。但是,考虑到这一点,将声明放入循环中是完全有效的,并且很明显。那就是您凌晨4点打高尔夫球的收获:^)
Marv

3
@Joba很确定那是无效的,因为在找不到字符串时indexOf返回-1;这将需要indexOf(a[i])==0在这种情况下,没有积蓄。
Pokechu22

6

Python 2,48 51字节

lambda l:all(1/map(a.find,l).count(0)for a in l)

对于每个元素al,该函数a.find发现的第一个匹配的索引a输入字符串,给人-1为不存在。因此,0表示前缀。在一个无前缀列表,映射此功能只返回一个单一0a本身。该函数检查是否每个情况都如此a


51个字节:

lambda l:[a for a in l for b in l if b<=a<b+'~']==l

替换~为ASCII码为128或更高的字符。

对于每一个元素al,包含一份针对每一个是它的前缀元素。对于无前缀列表,唯一这样的元素是a自身,因此这给出了原始列表。


4

CJam,14个字节

q~$W%2ew::#0&!

测试套件。

说明

q~   e# Read and evaluate input.
$    e# Sort strings. If a prefix exists it will end up directly in front 
     e# of a string which contains it.
W%   e# Reverse list.
2ew  e# Get all consecutive pairs of strings.
::#  e# For each pair, find the first occurrence of the second string in the first.
     e# If a prefix exists that will result in a 0, otherwise in something non-zero.
0&   e# Set intersection with 0, yielding [0] for falsy cases and [] for truthy ones.
!    e# Logical NOT.

4

的JavaScript ES6,65个 43 40字节

a=>!/(.*)\1/.test(''+a.sort().join``)
      ^            ^               ^ embedded NUL characters

我以前的解决方案,处理所有UTF-8字符的字符串数组:

a=>!/[^\\]("([^"]*\\")*[^\\])",\1/.test(JSON.stringify(a.sort()))

JSON.stringify由于挑战仅指定可打印的ASCII字符,因此我能够避免。

测试

f=a=>!/(\0.*)\1/.test('\0'+a.sort().join`\0`) // since stackexchange removes embedded NUL characters

O.textContent += 'OK: '+
[["Hello", "World"]                      
,["Code", "Golf", "Is", "Cool"]
,["1", "2", "3", "4", "5"]
,["This", "test", "case", "is", "true"]          
,["111", "010", "000", "1101", "1010", "1000", "0111", "0010", "1011", 
 "0110", "11001", "00110", "10011", "11000", "00111", "10010"]
].map(a=>f(a)) 

O.textContent += '\nKO: '+
[["4", "42"]                             
,["1", "2", "3", "34"]                   
,["This", "test", "case", "is", "false", "t"]
,["He", "said", "Hello"]
,["0", "00", "00001"]
,["Duplicate", "Duplicate", "Keys", "Keys"]
].map(a=>f(a))
<pre id=O></pre>


3

Haskell,49个字节

g x=[1|z<-map((and.).zipWith(==))x<*>x,z]==(1<$x)

这包括两个部分:

-- Are two lists (or strings) equal for their first min(length_of_1,length_of_2) elements, i.e. is one the prefix of the other?
(and.).zipWith(==)

-- Check whether one element is the prefix of the other, for all pairs of elements (including equal pairs)
map((and.).zipWith(==))x<*>x

-- This is a list of 1's of length (number of elements that are the prefix of the other)
[1|z<-map((and.).zipWith(==))x<*>x,z]

-- This is the input list, with all the elements replaced with 1's
(1<$x)

如果两个列表相等,则元素只是其自身的前缀,并且有效。


3

视网膜,19字节

字节数假定为ISO 8859-1编码。

O`.+
Mm1`^(.+)¶\1
0

输入应以换行分隔。输出是0虚假的和1真实的。

在线尝试!(稍作修改以支持多个以空格分隔的测试用例。)

说明

O`.+

对输入中的行进行排序。如果存在前缀,它将直接终止于包含该前缀的字符串的前面。

Mm1`^(.+)¶\1

尝试匹配(M)完整的行,该行也位于下一行的开头。所述m激活多行模式,使得^匹配线的起点和1确保我们只计数至多一个匹配,使得输出是01

0

为了交换01在结果中,我们算的数0秒。


3

Java,97个字节

Object a(String[]a){for(String t:a)for(String e:a)if(t!=e&t.startsWith(e))return 1<0;return 1>0;}

使用在中找到的大多数技巧 @Marv的答案中,但也使用了foreach循环和字符串引用相等。

未缩小:

Object a(String[]a){
    for (String t : a)
        for (String e : a)
            if (t != e & t.startsWith(e))
                return 1<0;
    return 1>0;
}

请注意,正如我所说,这使用字符串引用相等。那确实意味着由于String interning,代码的行为可能会很奇怪。当使用从命令行传递的参数时,以及使用从命令行读取的内容时,该代码都可以工作。但是,如果您想对值进行硬编码以进行测试,则需要手动调用String构造函数以强制进行实习:

System.out.println(a(new String[] {new String("Hello"), new String("World")}));
System.out.println(a(new String[] {new String("Code"), new String("Golf"), new String("Is"), new String("Cool")}));
System.out.println(a(new String[] {new String("1"), new String("2"), new String("3"), new String("4"), new String("5")}));
System.out.println(a(new String[] {new String("This"), new String("test"), new String("case"), new String("is"), new String("true")}));
System.out.println(a(new String[] {new String("111"), new String("010"), new String("000"), new String("1101"), new String("1010"), new String("1000"), new String("0111"), new String("0010"), new String("1011"), new String("0110"), new String("11001"), new String("00110"), new String("10011"), new String("11000"), new String("00111"), new String("10010")}));
System.out.println(a(new String[] {new String("4"), new String("42")}));
System.out.println(a(new String[] {new String("1"), new String("2"), new String("3"), new String("34")}));
System.out.println(a(new String[] {new String("This"), new String("test"), new String("case"), new String("is"), new String("false"), new String("t")}));
System.out.println(a(new String[] {new String("He"), new String("said"), new String("Hello")}));
System.out.println(a(new String[] {new String("0"), new String("00"), new String("00001")}));
System.out.println(a(new String[] {new String("Duplicate"), new String("Duplicate"), new String("Keys"), new String("Keys")}));

@Jo King请看我答案的后半部分;这有点复杂,取决于输入的指定方式。我不记得实际写过什么,但是
Pokechu22

3

PostgreSQL的,186,173个字节

WITH y AS(SELECT * FROM t,LATERAL unnest(c)WITH ORDINALITY s(z,r))
SELECT y.c,EVERY(u.z IS NULL)
FROM y LEFT JOIN y u ON y.i=u.i AND y.r<>u.r AND y.z LIKE u.z||'%' GROUP BY y.c

输出:

enter image description here

这次没有现场演示。http://sqlfiddle.com仅支持9.3,并且需要运行此演示9.4。

怎么运行的:

  1. 用数字分割字符串数组并命名 y
  2. 得到所有y
  3. LEFT OUTER JOIN到基于相同i(id)的相同派生表,但oridinal以前缀开头的不同y.z LIKE u.z||'%'
  4. 基于c(初始数组)对结果进行EVERY分组并使用分组功能。如果第二个表中的每一行都IS NULL表示没有前缀。

如果有人感兴趣,请输入:

CREATE TABLE t(i SERIAL,c text[]);

INSERT INTO t(c)
SELECT '{"Hello", "World"}'::text[]
UNION ALL SELECT  '{"Code", "Golf", "Is", "Cool"}'
UNION ALL SELECT  '{"1", "2", "3", "4", "5"}'
UNION ALL SELECT  '{"This", "test", "case", "is", "true"}'         
UNION ALL SELECT  '{"111", "010", "000", "1101", "1010", "1000", "0111", "0010", "1011","0110", "11001", "00110", "10011", "11000", "00111", "10010"}'
UNION ALL SELECT  '{"4", "42"}'
UNION ALL SELECT  '{"1", "2", "3", "34"}'                   
UNION ALL SELECT  '{"This", "test", "case", "is", "false", "t"}'
UNION ALL SELECT  '{"He", "said", "Hello"}'
UNION ALL SELECT  '{"0", "00", "00001"}'
UNION ALL SELECT  '{"Duplicate", "Duplicate", "Keys", "Keys"}';

编辑:

SQL Server 2016+ 实施:

WITH y AS (SELECT *,z=value,r=ROW_NUMBER()OVER(ORDER BY 1/0) FROM #t CROSS APPLY STRING_SPLIT(c,','))
SELECT y.c, IIF(COUNT(u.z)>0,'F','T')
FROM y LEFT JOIN y u ON y.i=u.i AND y.r<>u.r AND y.z LIKE u.z+'%' 
GROUP BY y.c;

LiveDemo

注意:这是逗号分隔的列表,不是实数数组。但主要思想与中的相同PostgreSQL


编辑2:

实际上WITH ORDINALITY可以被替换:

WITH y AS(SELECT *,ROW_NUMBER()OVER()r FROM t,LATERAL unnest(c)z)
SELECT y.c,EVERY(u.z IS NULL)
FROM y LEFT JOIN y u ON y.i=u.i AND y.r<>u.r AND y.z LIKE u.z||'%' GROUP BY y.c

SqlFiddleDemo


3

Brachylog,8个字节

¬(⊇pa₀ᵈ)

在线尝试!

通过谓词成功/失败输出。在最后一个真实的测试用例上花费超过60秒的时间,["111","010","000","1101","1010","1000","0111","0010","1011","0110","11001","00110","10011","11000","00111","10010"] 但是通过增加一个字节快速传递它,这比程序在其他情况下更早消除了很多可能性(Ċ在检查排列之前而不是在检查排列之后,将子列表的长度限制为二)。

¬(     )    It cannot be shown that
   p        a permutation of
  ⊇         a sublist of the input
      ᵈ     is a pair of values [A,B] such that
    a₀      A is a prefix of B.

少琐碎9字节的变体比¬(⊇Ċpa₀ᵈ)其运行在合理的时间是¬(⊇o₁a₀ᵈ)¬(⊇o↔a₀ᵈ),和¬(⊇oa₀ᵈ¹)


如果此挑战使用“两个不同且一致的值”而不是“真实/虚假”,则仅占用5个字节。
不相关的字符串

2

Perl 6,24个字节

{.all.starts-with(.one)}

在线尝试!

哇,使用长型内置扬声器的时间短得令人惊讶。

说明

{                      }  # Anonymous code block taking a list
 .all                     # Do all of the strings
     .starts-with(    )   # Start with
                  .one    # Only one other string (i.e. itself)

我写了一个50字节的答案,但您的答案却是从水里吹出来的。
bb94

1
@ bb94是的,我从一个类似的答案开始,但是遇到了与您相同的问题,其中包含重复键返回真值的集合。写下这个答案令人难以置信
Jo King

1

球拍,70个字节

(λ(l)(andmap(λ(e)(not(ormap(curryr string-prefix? e)(remv e l))))l))

1

Python,58 55字节

lambda l:sum(0==a.find(b)for a in l for b in l)==len(l)

a.index(b)==0有点短。或者,您可以这样做0**sum(a.index(b)for a in l for b in l)
Mego

@Mego那行不通,因为找不到indexb会引发异常。而且因为应该这样==,不是>=。但是,find可行。(而且它也更短!)
DJMcMayhem

糟糕,我的意思是打字find。昏昏欲睡的大脑很困。第二个版本也应与一起使用find
Mego

@Mego我不确定是否能获得第二版。那不是总是返回0吗?
DJMcMayhem

@Mego仅在每个字符串都相同的情况下才有效。之所以将之与之比较len(l)是因为我们要遍历b每个上的所有s,每个a总是至少有一个匹配a。因此,我们检查匹配数是否与元素数相同。
DJMcMayhem

1

JavaScript(ES6),52 54

编辑保存的2个字节thx @Neil

a=>!a.some((w,i)=>a.some((v,j)=>i-j&&!w.indexOf(v)))

测试

f=a=>!a.some((w,i)=>a.some((v,j)=>i-j&&!w.indexOf(v)))

O.textContent += 'OK: '+
[["Hello", "World"]                      
,["Code", "Golf", "Is", "Cool"]
,["1", "2", "3", "4", "5"]
,["This", "test", "case", "is", "true"]          
,["111", "010", "000", "1101", "1010", "1000", "0111", "0010", "1011", 
 "0110", "11001", "00110", "10011", "11000", "00111", "10010"]
].map(a=>f(a)) 

O.textContent += '\nKO: '+
[["4", "42"]                             
,["1", "2", "3", "34"]                   
,["This", "test", "case", "is", "false", "t"]
,["He", "said", "Hello"]
,["0", "00", "00001"]
,["Duplicate", "Duplicate", "Keys", "Keys"]
].map(a=>f(a))
<pre id=O></pre>


!w.indexOf(v)
尼尔,2016年

@尼尔,对了,谢谢
edc65 '16

1

Mathematica 75 69 68字节

像往常一样qua。但是Martin B能够将代码减少7个字节。

方法1:将输出存储在 Array

(68字节)

f@a_:=!Or@@(Join@@Array[a~Drop~{#}~StringStartsQ~a[[#]]&,Length@a])

f@{"111", "010", "000", "1101", "1010", "1000", "0111", "0010", "1011", "0110", "11001", "00110", "10011", "11000", "00111", "10010"}

真正


f@{"He", "said", "Hello"}


方法2:将输出存储在 List

(69字节)

f@a_:=!Or@@Flatten[a~Drop~{#}~StringStartsQ~a[[#]]&/@Range@Length@a]

优先规则应该a~Drop~{#}~StringStartsQ~a[[#]]起作用。还Array应该节省一些字节以上Length,特别是因为它可以让你使用Join@@,而不是Flatten@(提供的,你使用Flatten只为一个级别)。
马丁·恩德

谢谢你的建议。我稍后再调查Array
DavidC

1

Mathematica,41个字节

!Or@@StringStartsQ@@@Reverse@Sort@#~Subsets~{2}&

1

APL(Dyalog Unicode),13 字节SBCS

-2个字节:

≢=∘≢∘⍸∘.(⊃⍷)⍨

在线尝试!

说明:

≢=∘≢∘⍸∘.(⊃⍷)⍨   Monadic function train
               "Find": Convert the right argument into a boolean vector,
                where ones correspond to instances of the left argument
              Take the first item of the above vector (i.e., only prefixes)
     ∘.(  )⍨   Commutative outer product: take the above function and apply
               it for each possible pair of elements in the input
               If the input is a prefix code, the above should have a number of ones
               equal to the length of the input (i.e., each item is a prefix of only itself)
               To test this...
              Find the location of all ones in the above
   ≢∘          Take the length of the above
≢=∘            Compare to the length of the input

~2∊+\⊃¨∘.⍷⍨⎕­
ngn

1

J,17个字节

#=1#.1#.{.@E.&>/~

在线尝试!

注意:我实际上是在查看APL答案之前写的,以便在没有偏见的情况下进行处理。事实证明,方法几乎相同,这很有趣。我猜这是自然的“ array thinknig”解决方案

使用带框的输入,因为字符串长度不相等。

创建一个/~与每个元素配对的每个元素的自我功能表,并查看开头是否有匹配项{.@E.。这将产生1-0结果的矩阵。

对其求和两次1#.1#.,得到一个代表“矩阵中所有数字”的数字,然后看该数字是否与输入的长度相同#=。如果是,则唯一的前缀匹配项是自匹配项,即,我们有一个前缀代码。

排序解决方案,18字节

0=1#.2{.@E.&>/\/:~

尝试不同的方法。该解决方案对相邻的对进行排序并查看。

在线尝试!


1

R,48个字节

function(s)sum(outer(s,s,startsWith))==length(s)

在线尝试!

说明:outer(s,s,startsWith)输出一个逻辑矩阵,检查是否s[i]为的前缀s[j]。如果s是前缀代码,则length(s)结果中完全有TRUE元素,与对角线元素相对应(s[i]是其自身的前缀)。


1
我发现了一堆其他48字节的替代方案,例如,function(s)all(colSums(outer(s,s,startsWith))<2)但它仍然startsWith是我不知道的功能!好发现。
朱塞佩

1
@Giuseppe我尝试了几种检查矩阵是否为单位矩阵的方法,但也无法在48个字节以下获取它。我以为这种方式最容易理解,但是我敢肯定有人会打败它!
罗宾·赖德

47字节的取反TRUEFALSE...
朱塞佩

@Giuseppe允许吗?当输入是有效的前缀代码时,规则会明确要求真实性。(同样,您的链接是48字节版本,但我想您的建议是==>。:
Robin Ryder


0

Ruby,48个字节

使用参数作为输入,使用stdout作为输出。

p !$*.map{a,*b=$*.rotate!
a.start_with? *b}.any?

0

Scala,71个字节

(s:Seq[String])=>(for{x<-s;y<-s}yield x!=y&&x.startsWith(y)).forall(!_)

0

球拍130字节

(define g #t)(for((n(length l)))(for((i(length l))#:unless(= i n))(when(string-prefix?(list-ref l i)(list-ref l n))(set! g #f))))g

取消高尔夫:

(define(f l)
  (define g #t)
  (for ((n (length l)))
    (for ((i (length l)) #:unless (= i n))
      (when (string-prefix? (list-ref l i) (list-ref l n))
        (set! g #f))))g)

测试:

(f [list "Hello" "World"])             
(f [list "Code" "Golf" "Is" "Cool"])
(f [list "1" "2" "3" "4" "5"])
(f [list "This" "test" "case" "is" "true"])          
(f [list "111" "010" "000" "1101" "1010" "1000" "0111" "0010" "1011" 
         "0110" "11001" "00110" "10011" "11000" "00111" "10010"])

(f [list "4" "42"])                             
(f [list "1" "2" "3" "34"])                   
(f [list "This" "test" "case" "is" "false" "t"])
(f [list "He" "said" "Hello"])
(f [list "0" "00" "00001"])
(f [list "Duplicate" "Duplicate" "Keys" "Keys"])

输出:

#t
#t
#t
#t
#t
#f
#f
#f
#f
#f
#f


0

05AB1E,13 个字节

2.ÆDí«ε`Å?}O_

太长了。最初我有一个9字节的解决方案,但是对于重复的密钥测试用例却失败了。

在线尝试验证所有测试用例

说明:

2.Æ             # Get all combinations of two elements from the (implicit) input-list
   Dí           # Duplicate and reverse each pair
     «          # Merge the lists of pairs together
      ε         # Map each pair to:
       `        #  Push both strings to the stack
        Å?      #  And check if the first starts with the second
          }O    # After the map: sum to count all truthy values
            _   # And convert it to truthy if it's 0 or falsey if it's any other integer
                # (which is output implicitly as result)

0

Japt,8字节

á2 ËrbÃe

试试吧

á2 ËrbÃe     :Implicit input of array
á2           :Permutations of length 2
   Ë         :Map each pair
    r        :  Reduce by
     b       :  Get the index of the second in the first - 0 (falsey) if it's a prefix
      Ã      :End map
       e     :All truthy (-1 or >0)


0

Stax,6 个字节

å·↑↑¶Ω

运行并调试

这会产生非零的真相。

总体思路是考虑输入中的每对字符串。如果一个子字符串索引中的另一个子字符串索引永远为零,则它不是有效的前缀代码。在stax中,不存在的子字符串的索引产生-1。这样,所有成对的子字符串索引都可以相乘。

这是与isaacg的pyth解决方案相同的算法,但我是独立开发的。

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.