Chia một từ thành nhiều phần có điểm bằng nhau


9

Giả sử A = 1, B = 2 ... Z = 26 và giá trị của một từ là tổng của các giá trị chữ cái này, có thể chia một số từ thành hai phần sao cho chúng có giá trị bằng nhau.

Ví dụ: "wordsplit" có thể được chia thành hai phần như vậy: ordsl wpit, vì o + r + d + s + l = w + p + i + t.

Đây là một thử thách được đưa ra cho chúng tôi bởi giáo viên tin học của tôi - rõ ràng đây là một thử thách Lionhead Studios. Tôi đã giải quyết nó bằng Python và sẽ sớm đăng câu trả lời của tôi.

Thử thách: Chương trình ngắn nhất có thể liệt kê tất cả các phần chia có thể có điểm bằng nhau. Lưu ý rằng nó chỉ phải liệt kê một cho mỗi nhóm chữ cái - ví dụ như ordsl wpit giống như ndosl wtip. Dễ dàng hơn để liệt kê chúng theo thứ tự chúng đến trong từ.

Tặng kem:

  • Nếu bạn đánh dấu các cặp trong đó cả hai từ đều là từ tiếng Anh hợp lệ (hoặc một số hoán vị của các chữ cái), sử dụng danh sách từ nào đó. (Điều này có thể được thực hiện bằng cách đặt một dấu sao bên cạnh mỗi hoặc một số phương thức khác, nhưng làm cho nó rõ ràng.)
  • Thêm tùy chọn để loại bỏ trùng lặp (điều này không phải là mặc định.)
  • Hỗ trợ nhiều hơn hai phần tách, ví dụ: chia ba, bốn hoặc thậm chí n-way.

Phải hỗ trợ chương trình đầu vào trường hợp hỗn hợp? Và nếu vậy nó có thể loại bỏ trường hợp cho đầu ra?
Nemo157

@ Nemo157 Nó có thể bỏ qua trường hợp và không phải bảo quản nó trên đầu ra.
Thomas O

Chương trình có thể xuất ra các công cụ bổ sung không, miễn là phần nào được yêu cầu của đầu ra rõ ràng đối với con người?
JB

@JB Vâng, nó có thể.
Thomas O

ok, sau đó tôi sẽ cải thiện Perl đó;) Cảm ơn
JB

Câu trả lời:


4

Perl, 115 118 123

@_=~/./g;for$i(1..1<<@_){$l=$
r;$i&1<<$_?$l:$r+=64-ord$_[$_
]for 0..$#_;$l-$r||$i&1<<$_&&
print$_[$_]for 0..$#_;say""}

Chạy với perl -nE '<code goes here>'. Đó là 'n' được tính trong kích thước mã.

Tôn trọng:

@_ = /./g;
for $i (1 .. 1<<@_) {
  $l = $r;
  $i & 1<<$_ ? $l : $r -= 64 - ord $_[$_] for 0 .. $#_;

  $l - $r      ||
  $i & 1<<$_   &&
  print $_[$_]
    for 0 .. $#_;

  say ""
}

Với ý kiến ​​và tên biến:

# split a line of input by character
@chars = /./g;

# generate all binary masks of same length
for $mask (1 .. 1<<@_) {

  # start at "zero"
  $left_sum = $right_sum;

  # depending on mask, choose left or right count
  # 1 -> char goes left; 0 -> char goes right
  $mask & 1<<$_ ? $left_sum : $right_sum
    -= 64 - ord $chars[$_]   # add letter value
      for 0 .. $#chars;      # for all bits in mask

  # if left = right
  $left_sum - $right_sum ||

  # if character was counted left (mask[i] = 1)
  $mask & 1<<$_          &&

  # print it
  print $chars[$_]

  # ...iterating on all bits in mask
    for 0 .. $#chars;

  # newline
  say ""
}

Một số thủ thuật được sử dụng:

  • 1..1<<@_bao gồm phạm vi bit giống như 0..(1<<@_)-1, nhưng ngắn hơn. (lưu ý rằng việc xem xét vấn đề từ xa hơn, bao gồm cả ranh giới phạm vi nhiều lần sẽ không dẫn đến kết quả đầu ra sai)
  • $ left_range và $ right_range không được đặt lại về số 0 "0" thực tế: vì cuối cùng chúng ta chỉ tích lũy và so sánh chúng, tất cả những gì chúng ta cần là chúng bắt đầu ở cùng một giá trị.
  • trừ đi 64-ord$_[$_]thay vì thêm ord$_[$_]-64chiến thắng một nhân vật vô hình: vì nó kết thúc bằng một dấu phân cách, nó làm cho khoảng trống trước forkhông cần thiết.
  • Perl cho phép bạn gán cho một biến được xác định bởi toán tử điều kiện ternary : cond ? var1 : var2 = new_value.
  • biểu thức boolean xích với &&||được sử dụng thay vì điều kiện thích hợp.
  • $l-$r ngắn hơn $l!=$r
  • sẽ tạo ra một dòng mới ngay cả trên các phần tách không cân bằng. Các dòng trống là ok theo quy tắc! Tôi hỏi!

Muốn giải thích cho những người trong chúng ta không nói tiếng ồn? Có vẻ như bạn sử dụng cách tiếp cận mặt nạ nhị phân tương tự như của tôi và tôi thấy 64 có nghĩa là '@' = 'A' - 1, và sau đó tôi đã mất khá nhiều.
dmckee --- ex-moderator mèo con

Chỉnh sửa này có tốt hơn không?
JB

Đẹp. Tôi cần suy nghĩ về việc tận dụng lợi thế của việc thêm từng số vào bên trái hoặc bên phải. Đáng lẽ phải rõ ràng, nhưng tôi đã bỏ lỡ nó.
dmckee --- ex-moderator mèo con

3

J (109)

~.(/:{[)@:{&a.@(96&+)&.>>(>@(=/@:(+/"1&>)&.>)#[),}.@(split~&.>i.@#@>)@<@(96-~a.&i.)"1([{~(i.@!A.i.)@#)1!:1[1

Đầu ra cho wordsplit:

┌─────┬─────┐
│lorw │dipst│
├─────┼─────┤
Diltw│oprs
├─────┼─────┤
│iptw │dlors│
├─────┼─────┤
│dlors│iptw
├─────┼─────┤
Oprs │diltw│
├─────┼─────┤
│dipst│lorw
└─────┴─────┘

Giải trình:

  • 1!:1[1: đọc một dòng từ stdin
  • ([{~(i.@!A.i.)@#): nhận được tất cả các hoán vị
  • "1: cho mỗi hoán vị:
  • (96-~a.&i.): nhận được điểm thư
  • }.@(split~&.>i.@#@>)@<: chia từng hoán vị của điểm số ở mỗi không gian có thể, ngoại trừ trước số đầu tiên và sau số cuối cùng
  • >(>@(=/@:(+/"1&>)&.>)#[): xem những hoán vị nào có một nửa phù hợp và chọn những
  • {&a.@(96&+)&.>: biến điểm số trở lại thành chữ
  • ~.(/:{[): loại bỏ các biến thể tầm thường (ví dụ ordsl wpitordsl wpti)

Một số câu trả lời của bạn là trùng lặp.
DavidC

@DavidCarraher: Chà, tôi bị mù hoặc đây không phải là câu trả lời gần đây của tôi. Tôi chưa bao giờ sao chép câu trả lời của người khác về mục đích, mặc dù bạn có thể đúng, tôi đã đăng ở đây trong khi say rượu và không nhớ cho đến khi tôi nhận được vô số lượt tải xuống và hóa ra tôi đã gửi một cái gì đó thậm chí không gần đúng. Nếu bạn thấy tôi hành vi sai trái, có thể để lại bình luận nơi tôi đang nói sai, thì tôi sẽ xóa mọi câu trả lời vi phạm; hoặc có thể downvote chúng như đó là những gì downvote dành cho.
bến tàu

Không có ý định nhẹ. Tôi chỉ đơn giản có nghĩa là, ví dụ, câu trả lời đầu tiên của bạn, {"lorw", "dipst"} là một bản sao của câu trả lời cuối cùng của bạn, {"dipst", "lorw"}. Chỉ có thứ tự của các từ là khác nhau.
DavidC

@DavidCarraher: whoops: PI nghĩ rằng bạn có nghĩa là tôi đã sao chép câu trả lời của ai đó ... dù sao câu hỏi này nói (nếu tôi giải thích đúng) để xóa các bản sao trong đó các phần riêng lẻ chỉ là hoán vị của nhau, nhưng không xóa những cái mà thứ tự của các bộ phận là khác nhau, tức là nếu {a,bc}đã được tìm thấy, để loại bỏ {a,cb}nhưng không loại bỏ {bc,a}. (Và tất nhiên tôi không bị xúc phạm, nếu tôi thực sự / đã / sao chép câu trả lời của ai đó tôi sẽ thích nó nếu ai đó chỉ ra điều đó.)
marinus

Bạn có vẻ đúng. Các hướng dẫn nói rằng bạn có thể bỏ qua thứ tự các từ ("Lưu ý rằng chỉ phải liệt kê một từ cho mỗi nhóm chữ cái"), nhưng họ không yêu cầu nó. Điều này có thể tiết kiệm cho tôi một vài nhân vật. Cảm ơn.
DavidC

2

c99 - 379 ký tự cần thiết

#include <stdio.h>
#include <string.h>
#include <ctype.h>
int s(char*w,int l,int m){int b,t=0;for(b=0;b<l;++b){t+=(m&1<<b)?toupper(w[b])-64:0;}return t;}
void p(char*w,int l,int m){for(int b=0;b<l;++b){putchar((m&1<<b)?w[b]:32);}}
int main(){char w[99];gets(w);int i,l=strlen(w),m=(1<<l),t=s(w,l,m-1);
for(i=0;i<m;i++){if(s(w,l,i)==t/2){p(w,l,i);putchar(9);p(w,l,~i);putchar(10);}}}

Cách tiếp cận là khá rõ ràng. Có một chức năng tổng hợp một từ theo mặt nạ và một chức năng in nó cũng theo mặt nạ. Đầu vào từ đầu vào tiêu chuẩn. Một điều kỳ lạ là thói quen in ấn chèn các khoảng trắng cho chữ không nằm trong mặt nạ. Một tab được sử dụng để phân tách các nhóm.

Tôi không làm gì trong số các mục thưởng, cũng không dễ dàng chuyển đổi để hỗ trợ chúng.

Đọc và nhận xét:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
int s(char *w, int l, int m){ /* word, length, mask */
  int b,t=0;                  /* bit and total */
  for (b=0; b<l; ++b){        
/*     printf("Summing %d %d %c %d\n",b,m&(1<<b),w[b],toupper(w[b])-'A'-1); */
    t+=(m&1<<b)?toupper(w[b])-64:0; /* Add to toal if masked (A-1 = @ = 64) */
  }
  return t;
}
void p(char *w, int l, int m){
  for (int b=0; b<l; ++b){ 
    putchar((m&1<<b)?w[b]:32);  /* print if masked (space = 32) */
  }
}
int main(){
  char w[99];
  gets(w);
  int i,l=strlen(w),m=(1<<l),t=s(w,l,m-1);
/*   printf("Word is '%s'\n",w); */
/*   printf("...length %d\n",l); */
/*   printf("...mask   0x%x\n",m-1); */
/*   printf("...total  %d\n",t); */
  for (i=0; i<m; i++){
/*     printf("testing with mask 0x%x...\n",i); */
    if (s(w,l,i)==t/2) {p(w,l,i); putchar(9); p(w,l,~i); putchar(10);}
    /* (tab = 9; newline = 10) */
  }
}

Thẩm định

 $ wc wordsplit_golf.c
  7  24 385 wordsplit_golf.c
 $ gcc -std=c99 wordsplit_golf.c
 $ echo wordsplit | ./a.out
warning: this program uses gets(), which is unsafe.
 or sp          w  d  lit
wor   l            dsp it
 ords l         w    p it
w    p it        ords l  
   dsp it       wor   l  
w  d  lit        or sp   

1

Ruby: 125 ký tự

r=->a{a.reduce(0){|t,c|t+=c.ord-96}}
f=r[w=gets.chomp.chars]
w.size.times{|n|w.combination(n).map{|s|p([s,w-s])if r[s]*2==f}}

Chạy mẫu:

bash-4.2$ ruby -e 'r=->a{a.reduce(0){|t,c|t+=c.ord-96}};f=r[w=gets.chomp.chars.to_a];w.size.times{|p|w.combination(p).map{|q|p([q,w-q])if r[q]*2==f}}' <<< 'wordsplit'
[["w", "o", "r", "l"], ["d", "s", "p", "i", "t"]]
[["w", "p", "i", "t"], ["o", "r", "d", "s", "l"]]
[["o", "r", "s", "p"], ["w", "d", "l", "i", "t"]]
[["w", "d", "l", "i", "t"], ["o", "r", "s", "p"]]
[["o", "r", "d", "s", "l"], ["w", "p", "i", "t"]]
[["d", "s", "p", "i", "t"], ["w", "o", "r", "l"]]

1

Toán học 123 111

Tìm tất cả các tập hợp con của từ có 1/2 "tổng ascii" của từ , d. Sau đó tìm thấy các bổ sung của các tập hợp con.

d = "CÔNG VIỆC"

{#, Complement[w, #]}&/@Cases[Subsets@#,x_/;Tr@x==Tr@#/2]&[Sort[ToCharacterCode@d - 64]];
FromCharacterCode[# + 64] & /@ %

{{"IPTW", "DLORS"}, {"LORW", "DIPST"}, {"OPRS", "DILTW"}, {"DILTW", "OPRS"}, {"DIPST", "LORW"} , {"DLORS", "IPTW"}}


1

J, 66 ký tự

Sử dụng các chữ số của số cơ sở 2 để chọn mọi tập hợp con có thể.

   f=.3 :'(;~y&-.)"{y#~a#~(=|.)+/"1((+32*0&>)96-~a.i.y)#~a=.#:i.2^#y'
   f 'WordSplit'
┌─────┬─────┐
│Worl │dSpit│
├─────┼─────┤
│Wdlit│orSp │
├─────┼─────┤
│Wpit │ordSl│
├─────┼─────┤
│ordSl│Wpit │
├─────┼─────┤
│orSp │Wdlit│
├─────┼─────┤
│dSpit│Worl │
└─────┴─────┘

0

Giải pháp của tôi là dưới đây. Nó là một gần như chống golf trong kích thước của nó, nhưng nó hoạt động rất tốt. Nó hỗ trợ phân chia n-way (mặc dù thời gian tính toán trở nên rất dài đối với bất kỳ khoảng hơn 3 lần phân tách) và nó hỗ trợ loại bỏ các bản sao.

class WordSplitChecker(object):
    def __init__(self, word, splits=2):
        if len(word) == 0:
            raise ValueError, "word too short!"
        if splits == 0:
            raise ValueError, "splits must be > 1; it is impossible to split a word into zero groups"
        self.word = word
        self.splits = splits

    def solve(self, uniq_solutions=False, progress_notifier=True):
        """To solve this problem, we first need to consider all the possible
        rearrangements of a string into two (or more) groups.

        It turns out that this reduces simply to a base-N counting algorithm,
        each digit coding for which group the letter goes into. Obviously
        the longer the word the more digits needed to count up to, so 
        computation time is very long for larger bases and longer words. It 
        could be sped up by using a precalculated array of numbers in the
        required base, but this requires more memory. (Space-time tradeoff.)

        A progress notifier may be set. If True, the default notifier is used,
        if None, no notifier is used, and if it points to another callable,
        that is used. The callable must take the arguments as (n, count, 
        solutions) where n is the number of iterations, count is the total 
        iteration count and solutions is the length of the solutions list. The
        progress notifier is called at the beginning, on every 1000th iteration, 
        and at the end.

        Returns a list of possible splits. If there are no solutions, returns
        an empty list. Duplicate solutions are removed if the uniq_solutions
        parameter is True."""
        if progress_notifier == True:
           progress_notifier = self.progress 
        solutions = []
        bucket = [0] * len(self.word)
        base_tuple = (self.splits,) * len(self.word)
        # The number of counts we need to do is given by: S^N,
        # where S = number of splits,
        #       N = length of word.
        counts = pow(self.splits, len(self.word))
        # xrange does not create a list in memory, so this will work with very
        # little additional memory.
        for i in xrange(counts):
            groups = self.split_word(self.word, self.splits, bucket)
            group_sums = map(self.score_string, groups)
            if len(set(group_sums)) == 1:
                solutions.append(tuple(groups))
            if callable(progress_notifier) and i % 1000 == 0:
                progress_notifier(i, counts, len(solutions))
            # Increment bucket after doing each group; we want to include the
            # null set (all zeroes.)
            bucket = self.bucket_counter(bucket, base_tuple)
        progress_notifier(i, counts, len(solutions))
        # Now we have computed our results we need to remove the results that
        # are symmetrical if uniq_solutions is True.
        if uniq_solutions:
            uniques = []
            # Sort each of the solutions and turn them into tuples.  Then we can 
            # remove duplicates because they will all be in the same order.
            for sol in solutions:
                uniques.append(tuple(sorted(sol)))
            # Use sets to unique the solutions quickly instead of using our
            # own algorithm.
            uniques = list(set(uniques))
            return sorted(uniques)
        return sorted(solutions)

    def split_word(self, word, splits, bucket):
        """Split the word into groups. The digits in the bucket code for the
        groups in which each character goes in to. For example,

        LIONHEAD with a base of 2 and bucket of 00110100 gives two groups, 
        "LIHAD" and "ONE"."""
        groups = [""] * splits
        for n in range(len(word)):
            groups[bucket[n]] += word[n]
        return groups

    def score_string(self, st):
        """Score and sum the letters in the string, A = 1, B = 2, ... Z = 26."""
        return sum(map(lambda x: ord(x) - 64, st.upper()))

    def bucket_counter(self, bucket, carry):
        """Simple bucket counting. Ex.: When passed a tuple (512, 512, 512)
        and a list [0, 0, 0] it increments each column in the list until
        it overflows, carrying the result over to the next column. This could
        be done with fancy bit shifting, but that wouldn't work with very
        large numbers. This should be fine up to huge numbers. Returns a new
        bucket and assigns the result to the passed list. Similar to most
        counting systems the MSB is on the right, however this is an 
        implementation detail and may change in the future.

        Effectively, for a carry tuple of identical values, this implements a 
        base-N numeral system, where N+1 is the value in the tuple."""
        if len(bucket) != len(carry):
            raise ValueError("bucket and carry lists must be the same size")
        # Increase the last column.
        bucket[-1] += 1 
        # Carry numbers. Carry must be propagated by at least the size of the
        # carry list.
        for i in range(len(carry)):
            for coln, col in enumerate(bucket[:]):
                if col >= carry[coln]:
                    # Reset this column, carry the result over to the next.
                    bucket[coln] = 0
                    bucket[coln - 1] += 1
        return bucket

    def progress(self, n, counts, solutions):
        """Display the progress of the solve operation."""
        print "%d / %d (%.2f%%): %d solutions (non-unique)" % (n + 1, counts, (float(n + 1) / counts) * 100, solutions) 

if __name__ == '__main__':
    word = raw_input('Enter word: ')
    groups = int(raw_input('Enter number of required groups: '))
    unique = raw_input('Unique results only? (enter Y or N): ').upper()
    if unique == 'Y':
        unique = True
    else:
        unique = False
    # Start solving.
    print "Start solving"
    ws = WordSplitChecker(word, groups)
    solutions = ws.solve(unique)
    if len(solutions) == 0:
        print "No solutions could be found."
    for solution in solutions:
        for group in solution:
            print group,
        print

Đầu ra mẫu:

Enter word: wordsplit
Enter number of required groups: 2
Unique results only? (enter Y or N): y
Start solving
1 / 512 (0.20%): 0 solutions (non-unique)
512 / 512 (100.00%): 6 solutions (non-unique)
dspit worl
ordsl wpit
orsp wdlit

1
Theo ý kiến ​​của tôi, những câu trả lời thừa nhận không có nỗ lực nào trong việc đáp ứng mục tiêu của câu hỏi ban đầu (mã brevity) có hiệu quả ngoài chủ đề. Bạn thừa nhận rằng câu trả lời này không liên quan đến code-golf, vì vậy thay vì đăng nó dưới dạng câu trả lời tôi khuyên bạn nên đăng nó ở một nơi khác và đặt một liên kết đến nó trong một nhận xét về câu hỏi.
Jeff Swensen

2
@Sugerman: Đây là một triển khai tham khảo, không phải là một nỗ lực để giành chiến thắng trong trò chơi. Tôi thích triển khai tham chiếu như câu trả lời hơn là chiếm không gian ở đầu trang và tôi thích chúng trên trang web để loại bỏ nguy cơ thối liên kết.
dmckee --- ex-moderator mèo con

@Sugerman: Tại sao không gửi nó? Có còn hơn không. Nó có thể được giảm thiểu, nhưng tại sao phải bận tâm - tôi thực sự không thể chấp nhận câu hỏi của riêng mình (ừm, tôi có thể , nhưng nó không theo tinh thần của nó.)
Thomas O

Bởi vì tại cơ sở của nó, đây là một trang web Câu hỏi & Trả lời. Một cái gì đó không có ý định như một câu trả lời không nên được đăng như vậy.
Jeff Swensen

1
Như tôi đã nói trong bình luận đầu tiên của mình, tôi đã liên kết với nó trong một bình luận về Câu hỏi hoặc vì bạn sở hữu Câu hỏi chỉnh sửa liên kết trong đó. Cũng không có gì sai khi gửi Câu trả lời cho Câu hỏi của riêng bạn miễn là bạn không tự động chấp nhận Câu trả lời của mình mà không xem xét tất cả những người khác (và kết quả bỏ phiếu).
Jeff Swensen

0

Lua - 195

a=io.read"*l"for i=0,2^#a/2-1 do z,l=0,""r=l for j=1,#a do b=math.floor(i/2^j*2)%2 z=(b*2-1)*(a:byte(j)-64)+z if b>0 then r=r..a:sub(j,j)else l=l..a:sub(j,j)end end if z==0 then print(l,r)end end

đầu vào phải ở dạng mũ:

~$ lua wordsplit.lua 
>WORDSPLIT
WDLIT   ORSP
DSPIT   WORL
WPIT    ORDSL

0

Con trăn - 127

w=rawinput()
for q in range(2**len(w)/2):
 a=[0]*2;b=['']*2
 for c in w:a[q%2]+=ord(c)-96;b[q%2]+=c;q/=2
 if a[0]==a[1]:print b

và ở đây là Phiên bản phân chia n với 182 byte không trùng lặp:

n,w=input()
def b(q):
 a=[0]*n;b=['']*n
 for c in w:a[q%n]+=ord(c)-96;b[q%n]+=c;q/=n
 return a[0]==a[1] and all(b) and frozenset(b)
print set(filter(None,map(b,range(n**len(w)/n))))

Đầu vào là:

3, 'wordsplit'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.