単語を同じスコアの部分に分割する


9

A = 1、B = 2 ... Z = 26で、単語の値がこれらの文字値の合計であるとすると、いくつかの単語を2つの部分に分割して、それらの値を等しくすることができます。

たとえば、 "wordsplit"は次のように2つの部分に分割できます:ordslwpit。o+ r + d + s + l = w + p + i + tだからです。

これは私のコンピューティングの先生から私たちに与えられた挑戦でした-それは明らかに古いLionhead Studiosの挑戦です。私はそれをPythonで解決しました。まもなく私の回答を投稿します。

課題:等しいスコアを持つすべての可能な分割をリストできる最短のプログラム。文字のグループごとに1つだけリストする必要があることに注意してください。たとえば、ordsl wpitはrdosl wtipと同じです。単語の中で来る順にリストする方が簡単です。

ボーナス:

  • 両方の単語が有効な英語の単語(または文字のいくつかの順列)であるペアを強調表示する場合は、何らかの単語リストを使用します。(これは、各メソッドまたは他のメソッドの隣にアスタリスクを配置することで実行できますが、明確にしてください。)
  • 重複を削除するオプションを追加します(これはデフォルトではありません)。
  • 3つ、4つ、またはn方向の分割など、3つ以上の分割をサポートします。

プログラムは大/小文字混合入力をサポートする必要がありますか?もしそうなら、それは出力のケースを破棄できますか?
Nemo157

@ Nemo157大文字と小文字を区別しない場合があり、出力で保持する必要はありません。
Thomas O

要求された出力部分が人間にとって明確である限り、プログラムは余分なものを出力できますか?
JB

@JBはい、できます。
Thomas O

では、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では、3項条件演算子によって決定される変数に割り当てることができますcond ? var1 : var2 = new_value
  • &&とチェーンされたブール式||は、適切な条件の代わりに使用されます。
  • $l-$r より短い $l!=$r
  • バランスが取れていない分割でも改行を出力します。空の行はルールでOKです!私は尋ねた!

ラインノイズを話さない私たちのために説明してくれませんか?私と同じようなバイナリマスクアプローチを使用しているように見えますが、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 wpitordsl wpti

回答の一部が重複しています。
DavidC 2013年

@DavidCarraher:まあ、私は盲目か、これはそうではなく、私の最近の答えでもありません。私は他の人の答えをわざとコピーしたことはありませんが、もちろん正しいかもしれませんが、酔っている間にここに投稿し、大量のダウン投票を取得するまで覚えていませんでした。修正に近い。私が誤動作しているのを見た場合は、おそらく私が誤動作しているところにコメントを残してください。問題のある回答はすべて削除します。または多分それを反対投票するためにそれらを反対投票します。
マリナス

少しも意図されていませんでした。たとえば、最初の回答{"lorw"、 "dipst"}は、最終的な回答{"dipst"、 "lorw"}の複製であることを単に意味します。単語の順序だけが異なります。
DavidC 2013年

@DavidCarraher:whoops:PIは私が誰かの答えをコピーするつもりだと思った...とにかくこの質問は(私がそれを正しく解釈していれば)個々の部分がお互いの順列である重複を削除するが削除はしないと言っていますパーツの順序が異なるもの、つまり{a,bc}既に見つかった場合は、削除する{a,cb}が削除しない{bc,a}。(そしてもちろん、私は気分を害していません。実際に誰かの回答を/ had /した場合、誰かがそれを指摘した方がいいです。)
marinus

あなたは正しいようです。指示には、単語の順序を無視しても問題はない(「文字のグループごとに1つだけリストする必要があることに注意してください」)とありますが、必須ではありません。これは私にいくつかの文字を節約するかもしれません。ありがとう。
DavidC 2013年

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

アプローチはかなり明白です。マスクに従って単語を合計する機能と、マスクに従ってそれを印刷する機能があります。標準入力からの入力。奇妙な点の1つは、印刷ルーチンがマスクにない文字のスペースを挿入することです。タブを使用してグループを分離します。

私はボーナスアイテムは何もしませんし、サポートするために簡単に変換されることもありません。

読みやすく、コメント:

#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

ルビ: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

Mathematica 123 111

単語の「アスキー合計」の1/2を持つ単語のすべてのサブセットを検索します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文字

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

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
私の意見では、元の質問(コードの簡潔さ)の目標を達成しようとする試みをゼロにしている回答は、事実上トピックから外れています。この回答はcode-golfとは関係がないことを認めているので、回答として投稿するのではなく、どこか別の場所に投稿して、質問へのコメントにリンクを貼ることをお勧めします。
Jeff Swensen、2011

2
@Sugerman:これはリファレンス実装であり、ゲームに勝つための試みではありません。ページ上部のスペースを占有するのではなく、リファレンス実装を回答として使用することを好みます。また、リンクが破損するリスクを取り除くために、オンサイトで実装することを好みます。
dmckee ---元モデレーターの子猫

@Sugerman:なぜそれを提出しないのですか?何もないよりはましだ。それは最小限に抑えられるかもしれませんが、なぜわざわざ-私は自分の質問を本当に受け入れることができません(まあ、私はできますが、それはその精神にありません。)
Thomas O

その根底にあるので、これは質疑応答サイトです。回答を目的としていないものは、そのように投稿しないでください。
Jeff Swensen、2011

1
最初のコメントで述べたように、質問へのコメントでリンクするか、質問を所有しているのでリンクを編集します。他のすべて(および投票結果)を考慮せずに自分の回答を自動的に受け入れない限り、自分の質問への回答の送信に問題はありません。
Jeff

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

そしてここに重複のない182バイトのn分割バージョン:

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'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.