하위 문자열에서 문자열 식별


20

소개

나는 이전에 만든 두 개의 도전 과제 아이디어는 가능한 한 적은 수의 쿼리 유형의 작업으로 사용하여 객체를 재구성하는 것입니다; 이것은 세번째가 될 것입니다.

작업

입력 내용 S은 알파벳 abc과 길이에 걸쳐 비어 있지 않은 문자열 이어야하며 출력은이어야합니다 S. 제한이 없다면, 이것은 사소한 작업 일 것입니다. 중요한 것은 당신이 S직접 액세스 할 수 없다는 것 입니다. 당신이 할 수있는 유일한 일은 S함수를 호출하는 것입니다 num_occur(T, S). 여기서는 T다른 문자열 num_occur이며 Tin 의 발생 횟수를 계산합니다 S. 중복 발생은 별개의 것으로 계산되므로 num_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다른 사람을 사용하지 않는 경우 구현을 공유하는 것이 좋습니다 . 공을 굴리기 위해 파이썬으로 구현했습니다.

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 좋은 질문입니다. 비어 있지 않은 모든 문자열에 이론적으로 작동해야합니다. 도전을 편집하겠습니다.
Zgarb

all non-empty strings어떤 길이의?
edc65

@ edc65 이론적으로 그렇습니다. 바운드 메모리 주소 및 기타 실제 제한 사항은 무시할 수 있습니다.
Zgarb

VW 알고리즘을 추가하여 성공적으로 평가 테스트를 통과 할 수 있습니다. abc_strings.txt의 알려진 문자열이 있는지 먼저 확인하십시오
Emmanuel

답변:


6

자바 스크립트, 14325 14311 호출

빈 문자열로 시작하여 현재 문자열의 끝에 또는 현재 문자열의 시작 부분에 새 문자를 추가하여 적어도 하나의 일치하는 항목을 재귀 적으로 진행합니다.

이전의 모든 결과 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

@KarlKastor-죄송합니다. 네가 옳아. 고정되어 있습니다. 감사!
Arnauld

4

파이썬, 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세트 currentcurrent_new단계 그밖에 2로 돌아가서, 우리는 다음 문자로 이동합니다.

  3. 길이가 길이와 current같으면 S완료되었다고 말할 수 있습니다. 그렇지 않으면 2 단계로 돌아가지만 current_new문자와 같도록 current대신 2.1 단계를 수정 하십시오 . 이 단계에 다시 도달하면 완료됩니다.


1
파이썬의 for 루프에는 else 절이 있습니다. 이것은 완벽한 유스 케이스입니다.
Jakube

4

Python 2, 14952 14754 호출

첫 번째 대답과 매우 유사하지만 다음 문자를 시도하지 않아 하위 문자열이 불가능합니다.

  • num_occur대상에서 발생하지 않는다는 것을 알고 있습니다 (이전 호출에서).

  • 우리는 이미 하위 문자열을 다음보다 더 자주 사용했습니다. num_occur

(분 안에 하위 문자열 수를 추가합니다) 완료

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

파이썬 12705 12632 전화

  1. 2 개의 문자열로 된 목록을 만듭니다.
  2. 목록을 정렬
  3. 가장 가능성이 높은 문자를 먼저 시도하여 문자열을 작성하십시오. 한 가지 가능성이 있는지 테스트하지 마십시오.
  4. 목록을 업데이트
  5. 목록이 비어 있으면 2 단계가 완료됩니다.

Loovjo 함수 스켈레톤을 사용했습니다. 나는 파이썬으로 코딩하지 않았다. 나는 부트 랩이 필요했다.

편집 :
한 문자 길이 문자열에 대한 코드
추가 이미 일치하는 패턴을 거부하는 코드 추가

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

@Sparr "cc"는 100 개의 호출을 저장합니다 :`tuples.append ((len (S) -sum-1, "cc"))`
Emmanuel
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.