Membagi kata menjadi beberapa bagian dengan skor yang sama

9

Dengan asumsi A = 1, B = 2 ... Z = 26, dan nilai kata adalah jumlah dari nilai-nilai huruf ini, dimungkinkan untuk membagi beberapa kata menjadi dua bagian sehingga mereka memiliki nilai yang sama.

Misalnya, "wordsplit" dapat dipecah menjadi dua bagian seperti: ordsl wpit, karena o + r + d + s + l = w + p + i + t.

Ini adalah tantangan yang diberikan kepada kami oleh guru komputer saya - ini adalah tantangan lama Lionhead Studios. Saya telah menyelesaikannya dengan Python, dan akan segera mengirimkan jawaban saya.

Tantangan: Program terpendek yang dapat membuat daftar semua kemungkinan split yang memiliki skor sama. Perhatikan bahwa hanya perlu mencantumkan satu untuk setiap grup huruf - ordsl wpit sama dengan rdosl wtip, misalnya. Lebih mudah untuk mendaftar mereka dalam urutan mereka datang dalam kata.

Bonus:

  • Jika Anda menyorot pasangan di mana kedua kata tersebut adalah kata-kata bahasa Inggris yang valid (atau permutasi beberapa huruf), gunakan daftar kata dari beberapa jenis. (Ini bisa dilakukan dengan menempatkan tanda bintang di sebelah masing-masing atau beberapa metode lain, tetapi jelaskan.)
  • Menambahkan opsi untuk menghapus duplikat (ini seharusnya tidak menjadi default.)
  • Mendukung lebih dari dua perpecahan, misalnya tiga, empat atau bahkan perpecahan n-arah.
Thomas O
sumber
Haruskah program mendukung input kasus campuran? Dan jika demikian dapatkah itu membuang kasing untuk output?
Nemo157
@ Nemo157 Mungkin mengabaikan case dan tidak harus menyimpannya pada output.
Thomas O
Bisakah program menampilkan hal-hal tambahan, selama apa yang diminta bagian dari output jelas bagi manusia?
JB
@ JB Ya bisa.
Thomas O
ok, saya akan memperbaiki Perl itu;) Terima kasih
JB

Jawaban:

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""}

Jalankan dengan perl -nE '<code goes here>'. Itu 'n' dihitung dalam ukuran kode.

Dipindahkan:

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

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

  say ""
}

Dengan komentar dan nama variabel:

# 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 ""
}

Beberapa trik yang digunakan:

  • 1..1<<@_mencakup rentang bit yang sama dengan 0..(1<<@_)-1, tetapi lebih pendek. (perhatikan bahwa mempertimbangkan masalah dari jauh, termasuk batas rentang beberapa kali tidak akan menghasilkan output yang salah)
  • $ left_range dan $ right_range tidak diatur ulang ke angka nol "0" yang sebenarnya: karena kita baru saja mengakumulasikan dan membandingkannya pada akhirnya, yang kita butuhkan hanyalah mereka mulai dengan nilai yang sama.
  • mengurangi 64-ord$_[$_]bukannya menambahkan ord$_[$_]-64kemenangan karakter yang tidak terlihat: karena diakhiri dengan pembatas, itu membuat ruang sebelum fortidak perlu.
  • Perl memungkinkan Anda menetapkan ke variabel ditentukan oleh operator kondisional terner: cond ? var1 : var2 = new_value.
  • ekspresi boolean dirantai dengan &&dan ||digunakan sebagai pengganti kondisi yang tepat.
  • $l-$r lebih pendek dari $l!=$r
  • akan menampilkan baris baru bahkan pada split yang tidak seimbang. Baris kosong ok oleh aturan! Saya bertanya!
JB
sumber
Mau jelaskan bagi kita yang tidak berbicara derau? Sepertinya Anda menggunakan pendekatan topeng biner yang mirip dengan milik saya, dan saya melihat 64 berarti '@' = 'A' - 1, dan setelah itu saya cukup banyak tersesat.
dmckee --- ex-moderator kitten
Apakah hasil edit ini lebih baik?
JB
Bagus. Saya perlu berpikir tentang mengambil keuntungan dari jumlah tambah setiap ke kiri atau jumlah yang tepat. Seharusnya sudah jelas, tapi aku melewatkannya.
dmckee --- ex-moderator kitten
3

J (109)

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

Output untuk wordsplit:

┌─────┬─────┐
│atau │dipst│
├─────┼─────┤
Iltdiltw│oprs │
├─────┼─────┤
│iptw │dlors│
├─────┼─────┤
Ldlors│iptw │
├─────┼─────┤
│oprs │diltw│
├─────┼─────┤
Ipdipst│lorw │
└─────┴─────┘

Penjelasan:

  • 1!:1[1: baca baris dari stdin
  • ([{~(i.@!A.i.)@#): dapatkan semua permutasi
  • "1: untuk setiap permutasi:
  • (96-~a.&i.): dapatkan skor surat
  • }.@(split~&.>i.@#@>)@<: pisahkan setiap permutasi dari skor di setiap ruang yang mungkin, kecuali sebelum yang pertama dan setelah angka terakhir
  • >(>@(=/@:(+/"1&>)&.>)#[): lihat permutasi mana yang memiliki belahan yang cocok dan pilih ini
  • {&a.@(96&+)&.>: kembalikan skor menjadi huruf
  • ~.(/:{[): hapus variasi sepele (mis. ordsl wpitdan ordsl wpti)
marinus
sumber
Beberapa jawaban Anda adalah duplikat.
DavidC
@ Davidvidarraher: Yah, entah aku buta atau yang ini tidak, juga bukan jawaban terakhirku. Saya tidak pernah menyalin jawaban orang lain dengan sengaja, meskipun Anda tentu saja bisa benar, saya telah memposting di sini saat mabuk kadang-kadang dan tidak ingat sampai saya mendapat banyak downvotes dan ternyata saya mengirimkan sesuatu yang bahkan tidak dekat dengan yang benar. Jika Anda pernah melihat saya berperilaku tidak pantas, mungkin tinggalkan komentar di tempat saya melakukan kesalahan, maka saya akan menghapus jawaban yang menyinggung; atau mungkin downvote mereka untuk itulah downvotes.
marinus
Tidak sedikit yang dimaksudkan. Saya hanya bermaksud, misalnya, bahwa jawaban pertama Anda, {"lorw", "dipst"} adalah duplikat dari jawaban akhir Anda, {"dipst", "lorw"}. Hanya urutan kata-katanya yang berbeda.
DavidC
@DavidCarraher: whoops: PI pikir Anda maksud saya telah menyalin jawaban seseorang ... lagi pula pertanyaan ini mengatakan (jika saya telah menafsirkannya dengan benar) untuk menghapus duplikat di mana masing-masing bagian hanya permutasi satu sama lain, tetapi tidak untuk menghapus yang mana urutan bagian-bagiannya berbeda, yaitu jika {a,bc}sudah ditemukan, untuk menghapus {a,cb}tetapi tidak menghapus {bc,a}. (Dan tentu saja saya tidak tersinggung, jika saya benar-benar / telah menggandakan jawaban seseorang, saya lebih suka jika seseorang menunjukkannya.)
marinus
Kamu sepertinya benar. Instruksi mengatakan bahwa tidak masalah untuk mengabaikan urutan kata-kata ("Perhatikan bahwa itu hanya perlu daftar satu untuk setiap kelompok huruf"), tetapi mereka tidak memerlukannya. Ini mungkin menyelamatkan saya beberapa karakter. Terima kasih.
DavidC
2

c99 - 379 karakter yang diperlukan

#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);}}}

Pendekatannya cukup jelas. Ada fungsi yang menjumlahkan kata berdasarkan topeng dan fungsi yang mencetaknya juga berdasarkan topeng. Masukan dari input standar. Satu keanehan adalah bahwa rutinitas pencetakan menyisipkan spasi untuk huruf yang tidak ada dalam topeng. Tab digunakan untuk memisahkan grup.

Saya tidak melakukan satu pun item bonus, juga tidak mudah dikonversi untuk mendukungnya.

Dapat dibaca dan dikomentari:

#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) */
  }
}

Validasi

 $ 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   
dmckee --- mantan kucing moderator
sumber
1

Ruby: 125 karakter

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}}

Contoh dijalankan:

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"]]
manatwork
sumber
1

Mathematica 123 111

Menemukan semua himpunan bagian dari kata yang memiliki 1/2 "total ascii" dari kata d,. Kemudian temukan pelengkap dari himpunan bagian tersebut.

d = "WORDSPLIT"

{#, 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"}}

DavidC
sumber
1

J, 66 karakter

Menggunakan digit angka base2 untuk memilih setiap subset yang mungkin.

   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 │
└─────┴─────┘
randomra
sumber
0

Solusi saya ada di bawah ini. Ini hampir anti-golf dalam ukurannya, tetapi ini bekerja dengan sangat baik. Ini mendukung pemisahan n-way (meskipun waktu komputasi menjadi sangat lama untuk lebih dari sekitar 3 splits) dan mendukung penghapusan duplikat.

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

Output sampel:

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
Thomas O
sumber
1
Menurut pendapat saya jawaban yang diakui tanpa upaya untuk memenuhi tujuan dari pertanyaan awal (kode singkat) secara efektif di luar topik. Anda mengakui bahwa jawaban ini tidak ada hubungannya dengan kode-golf jadi daripada mempostingnya sebagai jawaban saya sarankan mempostingnya di tempat lain dan menempatkan tautan di dalamnya dalam komentar pada pertanyaan.
Jeff Swensen
2
@ Sugerman: Ini implementasi referensi, bukan upaya untuk memenangkan permainan. Saya lebih suka implementasi referensi sebagai jawaban daripada mengambil ruang di bagian atas halaman, dan saya lebih suka mereka di tempat untuk menghilangkan risiko pembusukan tautan.
dmckee --- ex-moderator kitten
@ Sugerman: Mengapa tidak mengirimkannya? Lebih baik daripada tidak sama sekali. Itu bisa diminimalkan, tetapi mengapa repot-repot - Saya tidak bisa benar-benar menerima pertanyaan saya sendiri (well, saya bisa , tapi itu tidak sesuai semangatnya.)
Thomas O
Karena pada dasarnya, ini adalah situs Tanya & Jawab. Sesuatu yang tidak dimaksudkan sebagai jawaban tidak boleh diposting seperti itu.
Jeff Swensen
1
Seperti yang saya katakan di komentar pertama saya, saya akan menautkannya dalam komentar pada Pertanyaan atau karena Anda memiliki Pertanyaan, edit tautan di sana. Tidak ada yang salah dengan mengirimkan Jawaban ke Pertanyaan Anda sendiri selama Anda tidak secara otomatis menerima Jawaban Anda sendiri tanpa mempertimbangkan yang lainnya (dan hasil pemungutan suara).
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

input harus dalam huruf besar:

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

Python - 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

dan di sini Versi n-split dengan 182 byte tanpa duplikat:

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))))

Masukan misalnya:

3, 'wordsplit'
Daniel
sumber