Розділіть слово на частини з рівними балами


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' зараховується в розмірі коду.

Розміщено:

@_ = /./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
  • виведе новий рядок навіть на розбиття, які не врівноважують. Порожні рядки нормально за правилами! Я запитав!

Хочете пояснити тим, хто не розмовляє з шумом? Схоже, що ви використовуєте підхід бінарної маски, подібний до мого, і я бачу, що 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│
├─────┼─────┤
Diltw│oprs │
├─────┼─────┤
│iptw │dlors│
├─────┼─────┤
│dlors│iptw │
├─────┼─────┤
│oprs │diltw│
├─────┼─────┤
│dipst│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   

1

Ruby: 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"]]

1

Математика 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"}}


1

J, 66 символів

Використовуючи цифри базових чисел для вибору кожного можливого набору.

   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

Моє рішення нижче. Це майже анти-гольф за своїми розмірами, але працює дуже добре. Він підтримує n-way розщеплення (хоча час обчислень стає дуже довгим для більш ніж 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
На мою думку, відповіді, які, мабуть, роблять нульову спробу досягти мети оригінального питання (стислості коду), фактично поза темою. Ви визнаєте, що ця відповідь не має відношення до коду-гольфу, тому замість того, щоб розмістити його як відповідь, я б запропонував опублікувати його десь в іншому місці та поставити посилання на нього в коментарі до питання.
Jeff Swensen

2
@Sugerman: Це референтна реалізація, а не спроба виграти гру. Я віддаю перевагу реалізаціям посилань як відповідям, а не займаючи місця у верхній частині сторінки, і я віддаю перевагу їх на місці, щоб усунути ризик гниття посилань.
dmckee --- кошеня колишнього модератора

@Sugerman: Чому його не подати? Це краще, ніж нічого. Це можна звести до мінімуму, але навіщо турбуватись - я не можу реально прийняти власне запитання (ну, можу , але це не в дусі.)
Thomas O

Тому що в основі цього - це сайт із запитаннями та відповідями. Щось, що не призначено як відповідь, не повинно розміщуватися як таке.
Джефф Свенсен

1
Як я вже говорив у своєму першому коментарі, я би зв'язав це з ним у коментарі до питання або оскільки у вас є питання, відредагуйте посилання там. Немає нічого поганого і з поданням відповіді на власне запитання, якщо ви автоматично не приймаєте власний відповідь, не враховуючи всіх інших (і результатів голосування).
Jeff Swensen

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-split з 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'
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.