Разбейте слово на части с равными баллами

9

Предполагая, что A = 1, B = 2 ... Z = 26, а значение слова является суммой этих буквенных значений, можно разбить некоторые слова на две части, чтобы они имели равные значения.

Например, «wordsplit» можно разбить на две части следующим образом: ordsl wpit, потому что o + r + d + s + l = w + p + i + t.

Это был вызов, который нам дал мой учитель информатики - очевидно, это старый вызов Lionhead Studios. Я решил это на Python, и вскоре опубликую свой ответ.

Задача: самая короткая программа, которая может перечислить все возможные сплиты, которые имеют равные оценки. Обратите внимание, что для каждой группы букв должен быть указан только один - например, ordsl wpit аналогичен rdosl wtip. Проще перечислить их в том порядке, в котором они встречаются в слове.

Бонус:

  • Если вы выделите пары, в которых оба слова являются действительными английскими словами (или есть некоторая перестановка букв), используйте какой-либо список слов. (Это можно сделать, поставив звездочку рядом с каждым или каким-либо другим методом, но сделайте это ясно.)
  • Добавление опции для удаления дубликатов (это не должно быть по умолчанию.)
  • Поддержка более двух разбиений, например, трех, четырех или даже n-сторонних.
Томас О
источник
Должна ли программа поддерживать смешанный ввод? И если да, то можно ли отказаться от дела для вывода?
Nemo157
@ Nemo157 Может игнорировать регистр и не должен сохранять его на выходе.
Томас О
Может ли программа выводить дополнительные данные, если запрошенная часть вывода понятна человеку?
JB
@JB Да, может.
Томас О
хорошо, тогда я улучшу этот Perl;) Спасибо
JB

Ответы:

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

Беги с perl -nE '<code goes here>'. Это 'n' считается в размере кода.

Respaced:

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

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

  say ""
}

С комментариями и именами переменных:

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

Некоторые из используемых трюков:

  • 1..1<<@_охватывает тот же битовый диапазон, что и 0..(1<<@_)-1, но он короче. (обратите внимание, что если рассматривать проблему издалека, в том числе многократные границы диапазона, то в любом случае это не приведет к неправильному выводу)
  • $ left_range и $ right_range не сбрасываются в фактический «0» числовой ноль: так как мы просто накапливаем и сравниваем их в конце, нам нужно только, чтобы они начинались с одного и того же значения.
  • вычитание 64-ord$_[$_]вместо добавления ord$_[$_]-64выигрывает невидимый символ: так как он заканчивается разделителем, он делает пространство перед forненужным.
  • Perl позволяет присвоить переменной , определяемой троичного условного оператора: cond ? var1 : var2 = new_value.
  • логические выражения с цепочкой &&и ||используются вместо соответствующих условных выражений .
  • $l-$r короче чем $l!=$r
  • выведет новую строку даже на несбалансированных разделениях. Пустые строки в порядке по правилам! Я попросил!
JB
источник
Не хочешь объяснить тем из нас, кто не говорит на шум линии? Похоже, вы используете подход с двоичной маской, похожий на мой, и я вижу, что 64 означает «@» = «A» - 1, и после этого я в значительной степени заблудился.
dmckee --- котенок экс-модератора
Это редактирование лучше?
JB
Ницца. Мне нужно подумать о том, чтобы воспользоваться преимуществом добавления каждого отсчета влево или вправо. Должно было быть очевидно, но я пропустил это.
dmckee --- котенок экс-модератора
3

J (109)

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

Выход для wordsplit:

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

Объяснение:

  • 1!:1[1: прочитать строку из stdin
  • ([{~(i.@!A.i.)@#): получить все перестановки
  • "1: для каждой перестановки:
  • (96-~a.&i.): получить буквенные оценки
  • }.@(split~&.>i.@#@>)@<: разделить каждую перестановку баллов в каждом возможном интервале, кроме как до первого и после последнего числа
  • >(>@(=/@:(+/"1&>)&.>)#[): посмотрите, какие перестановки имеют совпадающие половинки, и выберите их
  • {&a.@(96&+)&.>: превратить счет обратно в буквы
  • ~.(/:{[): удалить тривиальные вариации (например, ordsl wpitи ordsl wpti)
Мэринус
источник
Некоторые из ваших ответов являются дубликатами.
DavidC
@DavidCarraher: Ну, я либо слепой, либо нет, ни мои последние ответы. Я никогда не копировал намеренно ответы других людей, хотя вы, конечно, могли бы быть правы, я иногда публиковался здесь, будучи пьяным, и не вспоминал, пока не получил массу отрицательных голосов, и оказалось, что я представил что-то, что даже не было близко к правильному. Если вы видели, что я плохо себя веду, возможно, оставьте комментарий, где я плохо себя веду, тогда я удалю любые оскорбительные ответы; или, может быть, понизить их, так как это то, что отрицательные голоса для.
Маринус
Никаких незначительных не было предназначено Я просто имел в виду, например, что ваш первый ответ, {"lorw", "dipst"} является дубликатом вашего окончательного ответа, {"dipst", "lorw"}. Только порядок слов отличается.
DavidC
@DavidCarraher: whoops: PI думал, что вы имели в виду, что я скопировал чей-то ответ ... в любом случае, этот вопрос говорит (если я правильно понял), чтобы удалить дубликаты, где отдельные части являются просто перестановками друг друга, но не удалить те, где порядок частей отличается, т. е. если {a,bc}он уже найден, удалить, {a,cb}но не удалить {bc,a}. (И, конечно, я не обижаюсь, если я на самом деле / ​​имел / дублировал чей-то ответ, я бы предпочел, чтобы кто-то на это указал.)
marinus
Вы, кажется, правы. В инструкциях говорится, что можно игнорировать порядок слов («Обратите внимание, что для каждой группы букв должен быть указан только один»), но они этого не требуют. Это может спасти меня несколько персонажей. Спасибо.
DavidC
2

c99 - 379 необходимых символов

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

Подход довольно очевиден. Существует функция, которая суммирует слова в соответствии с маской, и функция, которая печатает их также в соответствии с маской. Ввод со стандартного ввода. Одна странность в том, что процедура печати вставляет пробелы для буквы, а не в маску. Вкладка используется для разделения групп.

Я не делаю ни одного из бонусных предметов, и его нелегко конвертировать для поддержки

Читается и комментируется:

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

Проверка

 $ 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 --- котенок экс-модератора
источник
1

Рубин: 125 символов

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

Образец прогона:

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
источник
1

Mathematica 123 111

Находит все подмножества слов , которые имеют 1/2 «ASCii общие» слова, d. Затем находит дополнения этих подмножеств.

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
источник
1

J, 66 символов

Использование цифр номеров base2 для выбора каждого возможного подмножества.

   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
источник
0

Мое решение ниже. Это почти анти-гольф по своим размерам, но он работает очень хорошо. Он поддерживает n-стороннее разбиение (хотя вычислительное время становится очень большим для более чем 3-х разбиений) и поддерживает удаление дубликатов.

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

Пример вывода:

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
По моему мнению, ответы, которые по общему признанию делают нулевую попытку достигнуть цели оригинального вопроса (краткость кода), фактически не по теме. Вы признаете, что этот ответ не имеет отношения к code-golf, поэтому вместо того, чтобы публиковать его как ответ, я бы предложил опубликовать его где-нибудь еще и добавить ссылку на него в комментарии к вопросу.
Джефф Свенсен
2
@Sugerman: Это эталонная реализация, а не попытка выиграть игру. Я предпочитаю ссылочные реализации в качестве ответов, а не занимаю место в верхней части страницы, и я предпочитаю их на месте, чтобы устранить риск гниения ссылок.
dmckee --- котенок экс-модератора
@Sugerman: Почему бы не отправить его? Лучше, чем ничего. Это может быть сведено к минимуму, но зачем беспокоиться - я не могу принять мой собственный вопрос (ну, я могу , но это не в духе этого.)
Томас О
Потому что по сути это сайт вопросов и ответов. То, что не предназначено в качестве ответа, не должно публиковаться как таковое.
Джефф Свенсен
1
Как я сказал в своем первом комментарии, я бы связался с ним в комментарии к Вопросу или, поскольку у вас есть Вопрос, отредактируйте ссылку там. Также нет ничего плохого в том, чтобы отправить ответ на свой собственный вопрос, если вы автоматически не принимаете свой собственный ответ без учета всех остальных (и результатов голосования).
Джефф Свенсен
0

Луа - 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

ввод должен быть заглавными буквами:

~$ lua wordsplit.lua 
>WORDSPLIT
WDLIT   ORSP
DSPIT   WORL
WPIT    ORDSL
МНИИП
источник
0

Питон - 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

а вот версия с n-разбиением с 182 байтами без дубликатов:

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

Ввод, например:

3, 'wordsplit'
Даниил
источник