部分文字列から文字列を特定します


20

前書き

以前に、できる限り少ないクエリタイプの操作を使用してオブジェクトを再構築するという考え方の2つの 課題を作成しました。これは3番目になります。

タスク

入力はSアルファベットabcとその長さの空でない文字列であり、出力はですS。制限なしで、これはもちろん簡単な作業です。問題は、S直接アクセスすることが許可されていないことです。できるのは、他の文字列でSあるfunctionを呼び出すことだけです。in の出現回数をカウントします。重複するオカレンスは個別としてカウントされるため、実際には次のようなインデックスの数を返します。num_occur(T, S)Tnum_occurTSnum_occur(T, S)i

S[i, i+1, …, i+length(T)-1] == T

たとえば、num_occur("aba", "cababaababb")を返し3ます。num_occur(S, S)を返すことにも注意してください1。の結果num_occur("", S)は未定義であり、空の文字列で関数を呼び出さないでください。

要するに、あなたが取る関数やプログラムを書く必要がありますSし、length(S)入力として、呼び出し、num_occurいくつかの短い文字列とにS何回か、再構築しS、その情報を返すことから。

ルールとスコアリング

あなたの目標はnum_occur、できるだけ少ない呼び出しを行うプログラムを書くことです。ではこのリポジトリは、という名前のファイルを見つけますabc_strings.txt。このファイルには、長さ50〜99の100行の文字列が含まれています。スコアはこれらの入力に対する呼び出しnum_occur合計数であり、スコアが低いほど優れています。ソリューションは、実行中にこの番号を追跡し、終了時に印刷することが望ましいです。文字列は、から一様にランダムな文字を選択して生成されabcます この文字列生成方法に対して最適化することはできますが、文字列自体は最適化できません

提出する前にテストケースでソリューションを実行する必要があることを除いて、時間制限はありません。ソリューションはS、テストケースだけでなく、有効な入力に対して機能する必要があります。

num_occur他の人を使用していない場合は、実装も共有することをお勧めします。ボールを転がすために、Pythonでの実装を次に示します。

def num_occur(needle, haystack):
    num = 0
    for i in range(len(haystack) - len(needle) + 1):
        if haystack[i : i + len(needle)] == needle:
            num += 1
    return num

私たちのアルゴリズムは、可能なすべての文字列に対して機能する必要がありますかS、それともテストケースに対してのみ機能する必要がありますか?
Loovjo

@Loovjo良い質問です。理論的には、空でないすべての文字列に対して機能するはずです。チャレンジを編集します。
ズガルブ

all non-empty stringsどんな長さの?
edc65

@ edc65理論的にははい。制限されたメモリアドレスおよびその他の実際的な制限は無視できます。
Zgarb

評価テストに合格するためにVWアルゴリズムを追加して成功することができます。abc_strings.txtの既知の文字列の出現を最初に確認します
Emmanuel

回答:


6

Javascript、14325 14311呼び出し

空の文字列から始めて、現在の文字列の末尾または先頭に新しい文字を追加することで、少なくとも1つの一致が残っている間に再帰的に進みます。

からの以前の結果はすべてオブジェクトにnumOccur()保存され、symこのデータを使用して、候補になり得ない新しい文字列をすぐに拒否します。

編集:常にで始まるため、文字列の'a'正確な数を常に知ってaいます。シーケンスのみaが欠落していることを検出すると、この情報を使用してプロセスを早期に終了します。また、ChromeとIEで無効だった正規表現を修正しました。

var test = [
  'ccccbcbbbbacbaaababbccaacbccaaaaccbccaaaaaabcbbbab',
  // etc.
];
var call = 0;

function guess(S, len) {
  var sym = {};
  recurse(S, len, "", sym);
  return sym.result;
}

function recurse(S, len, s, sym) {
  var dictionary = [];

  if(s == '' || (isCandidate(s, sym) && (sym[s] = numOccur(S, s)))) {
    if(s.length == len) {
      sym.result = s;
    }
    else if(sym['a'] && count(s, 'a') == sym['a'] - (len - s.length)) {
      dictionary = [ Array(len - s.length + 1).join('a') ];
    }
    else {
      dictionary = [ "a", "b", "c" ];
    }
    dictionary.some(function(e) {
      return recurse(S, len, s + e, sym) || recurse(S, len, e + s, sym);
    });
    return true;
  }
  return false;
}

function isCandidate(s, sym) {
  return sym[s] === undefined && Object.keys(sym).every(function(k) {
    return count(s, k) <= sym[k];
  });
}

function count(s0, s1) {
  return (s0.match(new RegExp(s1, 'g')) || []).length;
}

function numOccur(S, s) {
  call++;
  return count(S, s);
}

test.forEach(function(S) {
  if(guess(S, S.length) != S) {
    console.log("Failed for: '" + S + "'");
  }
});
console.log(call + " calls");

以下の完全な実行可能スニペット。


「要するに、その情報からSを再構築し、それを返す関数またはプログラムを書くべきです。」
カールカストール

@KarlKastor-おっと。あなたが正しい。それは修正されました。ありがとう!
アーナウルド

4

Python、15205呼び出し

def findS(S, len_s, alphabeth = "abc"):
    if len_s == 0:
        return ""
    current = ""
    add_at_start = True
    while len(current) < len_s:
        worked = False 
        for letter in alphabeth:
            if add_at_start:
                current_new = current + letter
            else:
                current_new = letter + current
            if num_occur(current_new, S) > 0:
                current = current_new
                worked = True
                break
        if not worked:
            add_at_start = False
    return current 

num_occur文字列がの部分文字列であるかどうかを確認するためだけに使用され、部分文字列のS量を実際にカウントするために使用することはないため、この送信はほとんど最適ではない可能性があります。

このアルゴリズムは、最後にcurrent等しい文字列を保存することで機能しますS。アルゴリズムのすべての手順は次のとおりです。

  1. current等しく設定します''

  2. アルファベットの各文字を調べて、次を実行します。

    2.1。新しい文字列を作成し、current_newそれに等しい文字を設定しcurrentます。

    2.2。を実行してcurrent_new含まれSているnum_occurかどうかを確認し、結果が1より大きいかどうかを確認します。

    2.3。にcurrent_new含まれている場合はS、ステップ2 に設定currentcurrent_newて戻ります。それ以外の場合は、次の文字に進みます。

  3. の長さがの長さにcurrent等しい場合、S完了したと言えます。それ以外の場合、ステップ2に戻りますcurrent_newが、current代わりに続く文字と等しくなるようにステップ2.1を変更します。再びこのステップに到達すると、完了です。


1
Pythonのforループにはelse節があります。これは完璧なユースケースです。
寂部

4

Python 2、14952 14754呼び出し

最初の回答と非常に似ていますが、次の文字を試さないため、次のような不可能な部分文字列が発生します。

  • num_occurターゲットからは発生しないことがわかっています(以前の呼び出しから)

  • 部分文字列は、 num_occur

(1分以内に部分文字列のカウントを追加します)完了

def get_that_string(h,l,alpha = "abc"):
    dic = {}
    s = ""
    ##costs 1 additional call per example, but its worth it
    char_list = [num_occur(j,h) for j in alpha[:-1]]
    char_list.append(l - sum(char_list))
    for y, z in zip(char_list,alpha):
        dic[z] = y
    end_reached = False
    while len(s) < l:
        for t in alpha:
            if not end_reached:
                neu_s = s + t
                substrings = [neu_s[i:]   for i in range(len(neu_s))]
            else:
                neu_s = t + s
                substrings = [neu_s[:i+1] for i in range(len(neu_s))]
            ## Test if we know that that can't be the next char
            all_in_d = [suff for suff in substrings if suff in dic.keys()]
            poss=all(map(dic.get,all_in_d))
            if poss:
                if not neu_s in dic.keys():
                    dic[neu_s] = num_occur(neu_s,h)
                if dic[neu_s] > 0:
                    s=neu_s
                    for suff in all_in_d:
                        dic[suff] -= 1
                    break
        else:
            end_reached = True
    ##print s
    return s


## test suite start
import urllib

def num_occur(needle, haystack):
    global calls
    calls += 1
    num = 0
    for i in range(len(haystack) - len(needle) + 1):
        if haystack[i : i + len(needle)] == needle:
            num += 1
    return num

calls = 0
url = "https://raw.githubusercontent.com/iatorm/random-data/master/abc_strings.txt"
inputs = urllib.urlopen(url).read().split("\n")
print "Check: ", inputs == map(lambda h: get_that_string(h, len(h)), inputs)
print "Calls: ", calls

4

Python 12705 12632呼び出し

  1. 2文字の文字列オカレンスのリストを作成します
  2. リストを並べ替える
  3. 最も可能性の高い文字を最初に試して文字列を作成し、可能性が1つだけかどうかをテストしない
  4. リストを更新する
  5. リストが空の場合は終了し、そうでない場合はステップ2

Loovjo関数のスケルトンを使用しました。ブートストラップが必要なPythonでコーディングすることはありません

編集:
1文字の長さの文字列の
コードを追加すでに一致したパターンを拒否するコードを追加

def finds(S):

    if len(S) == 0:
            return ""
    if len(S) == 1 
            if num_occur("a",S) == 1 :
                         return "a"
            if num_occur("b",S) == 1 :
                         return "b"
            return "c"
    tuples=[]
    alphabet=[ "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb"]
    for s in alphabet : tuples.append( (num_occur(s,S), s) )

    sum=0
    for (i,s) in tuples :   sum+=i
    tuples.append( (len(S)-sum-1, "cc") )
    tuples.sort(key=lambda x:(-x[0],x[1]))

    (i, current) = tuples[0]
    tuples[0] = (i-1, current)

    add_at_start = True
    nomatch=[]
    while len(tuples) > 0:
            worked = False
            tuples.sort(key=lambda x:(-x[0],x[1]))
            count=0
            if not add_at_start :
                    for (n, s) in tuples :
                            if s[0]==current[-1:] :         count+=1
            for i in range(len(tuples)):
                    (n, s)=tuples[i]
                    if add_at_start:
                            if current[0] == s[1] :
                                    current_new = s[0] + current
                                    possible=True
                                    for nm in nomatch :
                                            lng=len(nm)
                                            if current_new[0:lng] == nm :
                                                    possible=False
                                                    break
                                    if possible and num_occur(current_new, S) > 0:
                                            current = current_new
                                            worked = True
                                    else :
                                            nomatch.append(current_new)
                    else:
                            if current[-1:] == s[0] :
                                    current_new =  current + s[1]
                                    possible=True
                                    for nm in nomatch :
                                            lng=len(nm)
                                            if current_new[-lng:] == nm :
                                                    possible=False
                                                    break
                                    if count == 1 or (possible and num_occur(current_new, S) > 0) :
                                            current = current_new
                                            worked = True
                                    else :
                                            nomatch.append(current_new)
                    if worked :
                            if n == 1:
                                    del tuples[i]
                            else    :
                                    tuples[i] = (n-1, s)
                            break
            if not worked:
                    add_at_start = False
    return current

あなたのアルファベットに「cc」はありませんか?
スパー

@Sparr "cc"が計算され、100回の呼び出しが保存されます。 `tuples.append((len(S)-sum-1、" cc "))`
エマニュエル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.