스 와이프 유형 변환기


27

랩탑 타이핑의 다음 혁명은 2014 년 4 월 1 일 SwiftKey에 의해 발표되었습니다 . 그러나 스 와이 핑 나노 클론을 작성하는 첫 번째 사람이되고 싶지만 실제 텍스트 라이브러리에 대한 좋은 스 와이프 텍스트를 찾을 수 없으므로 기다릴 수 없으므로 여기에서 묻습니다.

태스크

텍스트를 스 와이프하여 실제 텍스트에 해당하는 프로그램을 작성하십시오. 예:

Input: hgrerhjklo
Output: hello

사용자가 할 때 :

여기에 이미지 설명을 입력하십시오

다른 예 :

Input: wertyuioiuytrtjklkjhgfd
Output: world

Input: poiuytrtyuioiugrewsasdfgbhnmkijnbg
Output: programming

Input: poiuygfdzxcvhjklkjhgres
Output: puzzles

Input: cvhjioiugfde
Output: code

Input: ghiolkjhgf
Output: golf

규칙

  • 프로그램은 stdin 또는 argv에서 스 와이프 한 '단어'를받습니다.
  • 스 와이프 된 입력의 첫 글자와 마지막 글자는 실제 단어의 첫 글자와 마지막 글자와 동일합니다.
  • 사용자가 합리적인 직선을 만들 것이라고 가정 할 수 있지만 샘플 데이터를 사용하여이를 확인할 수 있습니다 (샘플 데이터를 만들고 최종 테스트 데이터를 만들 것입니다)
  • 모호한 입력의 경우 두 출력 중 하나를 선택할 수 있지만 테스트 데이터에서 모든 모호성을 제거하려고합니다.
  • 이 단어는 단어 목록에 있지만 스 와이프합니다. 단어 목록은 현재 디렉토리에 있으며 읽을 수 있습니다 (줄 바꿈으로 구분, 이름 wordlist없음, 확장자 없음).
  • 스 와이프에는 소문자 알파벳 만 포함됩니다
  • 사용자가 키를 일시 중지하면 스 와이프에 중복 문자가 포함될 수 있습니다.
  • 프로그램은 stdout에서 출력해야합니다 (대소 문자는 중요하지 않음)
  • 프로그램은 0리턴 코드로 리턴 해야합니다.
  • 실행 명령, 컴파일 명령 (필요한 경우), 이름 및 사용할 입력 경로를 제공해야합니다.
  • 표준 허점이 적용되지만 도움이되지 않을 수 있습니다
  • 내장되지 않은 라이브러리는 허용되지 않습니다
  • 결정적이지 않은 골프 / 난독 처리 된 솔루션 선호
  • 파일 쓰기, 네트워킹 등이 없습니다.
  • 코드는 1 초 이내에 실행되어야합니다 (코드 당 한 번 실행)
  • 스코어링 실행은 4 개의 가상 코드 (2 개의 실제 코드)가있는 Intel i7 Haswell 프로세서에서 실행되므로 필요한 경우 스레드를 사용할 수 있습니다
  • 5000 바이트의 최대 코드 길이
  • 사용하는 언어에는 Linux 용 무료 (시험판이 아님) 버전이 있어야합니다 (중요한 경우 Linux Linux)

승리 기준

  • 우승자는 가장 정확한 솔루션입니다 ( 제공된 테스트 목록을 사용하여 제어 프로그램에 의해 평가됨)
  • 인기는 타이 브레이커
  • 점수 표는 며칠마다 업데이트됩니다
  • 시간 초과 및 충돌이 실패로 계산
  • 이 도전은 인기도에 따라 2 주 이상 지속될 것입니다.
  • 최종 점수는 무작위로 선택된 다른 단어 목록 (동일한 단어 목록에서 동일한 길이)을 사용합니다.

다른

현재 점수 보드

테스트 목록 ( 로그 ) :

Three Pass Optimizer:Errors: 0/250       Fails: 7/250        Passes: 243/250     Timeouts: 0/250     
Corner Sim:         Errors: 0/250       Fails: 9/250        Passes: 241/250     Timeouts: 0/250     
Discrete Fréchet Distance:Errors: 0/250       Fails: 17/250       Passes: 233/250     Timeouts: 0/250     
Turnaround:         Errors: 0/250       Fails: 18/250       Passes: 232/250     Timeouts: 0/250     
Direction Checker:  Errors: 0/250       Fails: 19/250       Passes: 231/250     Timeouts: 0/250     
Regex Solver:       Errors: 0/250       Fails: 63/250       Passes: 187/250     Timeouts: 0/250

testlist2 ( 로그 ) :

Corner Sim:         Errors: 0/250       Fails: 10/250       Passes: 240/250     Timeouts: 0/250     
Three Pass Optimizer:Errors: 2/250       Fails: 14/250       Passes: 234/250     Timeouts: 0/250     
Turnaround:         Errors: 0/250       Fails: 16/250       Passes: 234/250     Timeouts: 0/250     
Direction Checker:  Errors: 0/250       Fails: 17/250       Passes: 233/250     Timeouts: 0/250     
Discrete Fréchet Distance:Errors: 0/250       Fails: 18/250       Passes: 232/250     Timeouts: 0/250     
Regex Solver:       Errors: 0/250       Fails: 67/250       Passes: 183/250     Timeouts: 0/250

파이널 런

테스트 목록 ( 로그 ) :

Corner Sim:         Errors: 0/250       Fails: 14/250       Passes: 236/250     Timeouts: 0/250     
Three Pass Optimizer:Errors: 0/250       Fails: 18/250       Passes: 232/250     Timeouts: 0/250     
Direction Checker:  Errors: 0/250       Fails: 20/250       Passes: 230/250     Timeouts: 0/250     
Turnaround:         Errors: 0/250       Fails: 23/250       Passes: 227/250     Timeouts: 0/250     
Discrete Fréchet Distance:Errors: 0/250       Fails: 30/250       Passes: 220/250     Timeouts: 0/250     
Regex Solver:       Errors: 0/250       Fails: 55/250       Passes: 195/250     Timeouts: 0/250

모두와 hgfdsasdertyuiopoiuy swertyuiopoijnhg 잘 했어요!


"솔루션"이란 무엇입니까? 코드는 어디에 있습니까?
Doorknob



@Optimizer 다른 경우에 대해서는 확신 할 수 없지만 " p oiuytres a se r es a s d fghui o iugfd x cgu i ug c xs a sdfghjk l ku y "에는 순서를 제외하고 순서대로 "역설적으로"모든 문자가 포함됩니다 l. 두 배가되지 않습니다.
es1024

1
@Optimiser 글쎄, 나는 당신의 제출이 그것을 이길 것이라고 생각했지만, 그것은 단지 아래에있었습니다 (약간의 조정은 그것을 바꿨을 것입니다.). 받아 들일 수있을 것 같습니다. 그래서 ... (응답을받지 못하는 것 같습니다)? 나는 다른 사람을 받아들이고 싶지만 규칙을 따르지 않습니다 (좋은 생각이 없다면).
matsjoyce

답변:


12

자바 스크립트, ES6 세 패스 최적화, 112 187 235 240 241 243 231 개 234 패스

키 입력 시퀀스에서 중요한 키를 먼저 파악한 다음 3 개의 필터를 통해 시퀀스를 전달하는 3 패스 필터 :

  1. 중요한 키와 보조 키에서 느슨하게 형성된 RegEx 이것은 대부분의 키에 대한 올바른 결과를 제공합니다 (약 150)
  2. 중요한 키로 구성된 엄격한 RegEx입니다. 이것은 추가 85 시퀀스에 대한 올바른 결과를 제공합니다
  3. 밀접한 답변 사이의 모호성을 파악하기위한 세 번째 필터입니다. 이것은 40 % 모호한 경우에 작동합니다.

암호

keyboard = {
  x: {},
  y: ['  q      w      e      r      t      y      u      i      o      p',
      '    a      s      d      f      g      h      j      k      l',
      '        z      x      c      v      b      n      m'],
};
for (var i in keyboard.y)
  for (var y of keyboard.y[i])
    keyboard.x[y] = +i*7;
p = C => (x=keyboard.x[C],{x, y: keyboard.y[x/7].indexOf(C)})
angle = (L, C, R) => (
  p0 = p(L), p1 = p(C), p2 = p(R),
  a = Math.pow(p1.x-p0.x,2) + Math.pow(p1.y-p0.y,2),
  b = Math.pow(p1.x-p2.x,2) + Math.pow(p1.y-p2.y,2),
  c = Math.pow(p2.x-p0.x,2) + Math.pow(p2.y-p0.y,2),
  Math.acos((a+b-c) / Math.sqrt(4*a*b))/Math.PI*180
)
corner = (L, C, R, N, W) => {
  if (skip) {
    skip = false;
    return [];
  } 
  ngl = angle(L, C, R);
  if (ngl < 80) return [C + "{1,3}"]
  if (ngl < 115 && p(L).x != p(R).x && p(L).x != p(C) && p(R).x != p(C).x && Math.abs(p(L).y - p(R).y) < 5) return [C + "{0,3}"]
  if (ngl < 138) {
    if (N && Math.abs(ngl - angle(C, R, N)) < 6) {
      skip = true;
      return [L + "{0,3}", "([" + C + "]{0,3}|[" + R + "]{0,3})?", N + "{0,3}"]
    }
    return [C + "{0,3}"]
  }
  return ["([" + L + "]{0,3}|[" + C + "]{0,3}|[" + R + "]{0,3})?"]
}
f = S => {
  for (W = [S[0] + "{1,2}"],i = 1; i < S.length - 1; i++)
    W.push(...corner(S[i - 1], S[i], S[i + 1], S[i + 2], W))
  return [
    new RegExp("^" + W.join("") + S[S.length - 1] + "{1,3}$"),
    new RegExp("^" + W.filter(C=>!~C.indexOf("[")).join("") + S[S.length - 1] + "{1,3}$")
  ]
}
thirdPass = (F, C) => {
  if (!F[0]) return null
  F = F.filter((s,i)=>!F[i - 1] || F[i - 1] != s)
  FF = F.map(T=>[...T].filter((c,i)=>!T[i - 1] || T[i - 1] != c).join(""))
  if (FF.length == 1) return F[0];
  if (FF.length < 6 && FF[0][2] && FF[1][2] && FF[0][0] == FF[1][0] && FF[0][1] == FF[1][1])
    if (Math.abs(F[0].length - F[1].length) < 1)
      for (i=0;i<Math.min(F[0].length, FF[1].length);i++) {
        if (C.indexOf(FF[0][i]) < C.indexOf(FF[1][i])) return F[0]
        else if (C.indexOf(FF[0][i]) > C.indexOf(FF[1][i])) return F[1]
      }
  return F[0]
}
var skip = false;
SwiftKey = C => (
  C = [...C].filter((c,i)=>!C[i - 1] || C[i - 1] != c).join(""),
  skip = false, matched = [], secondPass = [], L = C.length, reg = f(C),
  words.forEach(W=>W.match(reg[0])&&matched.push(W)),
  words.forEach(W=>W.match(reg[1])&&secondPass.push(W)),
  matched = matched.sort((a,b)=>Math.abs(L-a.length)>Math.abs(L-b.length)),
  secondPass = secondPass.sort((a,b)=>Math.abs(L-a.length)>Math.abs(L-b.length)),
  first = matched[0], second = secondPass[0], third = thirdPass(secondPass.length? secondPass: matched, C),
  second && second.length >= first.length - 1? first != third ? third: second: third.length >= first.length ? third: first
)

// For use by js shell of latest firefox
print(SwiftKey(readline()));

이 코드는 이 페이지words 의 모든 단어의 배열 인 변수 가 있다고 가정합니다 .

작동중인 코드보기

실제 테스트 사례보기

위 링크는 모두 최신 Firefox (33 이상)에서만 작동합니다 (ES6으로 인해).


예! 껍질로 껍질을 벗기고 있습니다. 이제 올바른 keypos.csv파일을 사용할 수도 있습니다 . 사용 가능한 IO 기능은 developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/
matsjoyce

괜찮지 만 스 와이프는 내 키보드 각도로 만들어 지므로 원하는 선택입니다 (그러나 점수에 영향을 미치지 않은 것 같습니다!)
matsjoyce


240 패스가 뛰어납니다! 나는 모호성이 그러한 좋은 결과를 막을 것이라고 생각했을 것입니다. 최종 테스트 세트에서 이것이 어떻게 수행되는지 궁금합니다.
Emil

@ 에밀-그래, 나도 그것을 기다리고 있습니다.
Optimizer

9

루비, 정규식 찾기 - 30 140 176 180 182 187 179 183 개 패스

나중에 점수를 알아 볼게요. 키보드 레이아웃을 고려하지 않은 매우 순진한 솔루션은 다음과 같습니다.

words = File.readlines('wordlist').map(&:chomp)

swipe = ARGV.shift
puts words.select {|word| word[0] == swipe[0] &&
                          word[-1] == swipe[-1]}
          .select {|word|
              chars = [word[0]]
              (1..word.size-1).each {|i| chars << word[i] if word[i] != word[i-1]}
              swipe[Regexp.new('^'+chars.join('.*')+'$')]
          }.sort_by {|word| word.size}[-1]

ARGV에서 입력을 받아 결과를 인쇄합니다. 첫 단어와 마지막 문자로 단어 목록을 필터링하고 나머지 단어를 입력에 대해 시도하고 있습니다 (중복 문자 제거 및 ^g.*u.*e.*s$"추측" 과 같은 정규 표현식 사용 ). 여러 솔루션입니다.

다음과 같이 실행

ruby regex-solver.rb cvhjioiugfde

다른 사람은이 단계를 첫 번째 필터로 자유롭게 재사용하십시오. 나는 올바른 단어를 버리지 않을 것이라고 생각 하므로이 예비 검사는 더 나은 알고리즘을 위해 검색 공간을 크게 줄일 수 있습니다.

편집 : OP 제안에 따라 지금은 가장 긴 후보자를 선택하고 있는데, 이는 괜찮은 휴리스틱 인 것 같습니다.

또한 중복 된 문자를 상기시켜 준 es1024 덕분입니다.


끝난. 귀하의 로그는 github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/에 있습니다 . 문제는 가능한 해결책 중에서 무작위로 선택한다는 것입니다.
matsjoyce

나는 정규 표현식이 요구하는 것보다 두 배가 아닌 입력에 한 번만 나타나기 paradoxically때문에 서로 같은 두 개의 동일한 문자가있는 올바른 단어를 모두 버릴 수 있다고 생각합니다 l.
es1024

@ es1024, 아 감사합니다.이 알고리즘을 샌드 박스에서 처음 제안했을 때 실제로 알고 있었지만 어제 잊어 버렸습니다. 나중에 수정합니다.
Martin Ender

7

C ++, 별도 Fréchet 거리 - 201 220 222 232 232 패스

나에게,이 문제 는 불행히도 계산하기 매우 어려운 Fréchet Distance 를 요구했다.

재미를 위해서, 나는 컴퓨팅 이산 Fréchet 거리 (1994) 에서 Thomas Eiter와 Heikki Mannila가 묘사 한 이산 근사를 구현하여 문제에 접근하려고했습니다 .

처음에는 입력의 하위 시퀀스 인 목록의 모든 단어를 필터링하기 위해 다른 방법과 동일한 접근 방식을 사용하고 있습니다 (동일한 문자의 여러 순차적 발생도 설명 함). 그런 다음 모든 단어의 문자에서 문자로 다각형 커브를 중간 점으로 채우고 입력 커브와 일치시킵니다. 마지막으로 모든 거리를 단어의 길이로 나누고 최소 점수를받습니다.

지금까지이 방법은 기대했던대로 작동하지 않습니다 (코드 예제를 "chide"로 인식 함). 아직 찾지 못한 버그의 결과 일 수 있습니다. 그렇지 않으면, 또 다른 아이디어는 프레 체 거리의 다른 변형 ( "최대 개 가죽 끈 길이"대신 "평균")을 사용하는 것입니다.

편집 : 이제 "평균 개 가죽 끈 길이"에 대한 근사치를 사용하고 있습니다. 이것은 모든 거리의 합을 최소화하고 나중에 거리 수로 나누는 두 경로 사이에 정렬 된 매핑을 찾고 있음을 의미합니다.

동일한 단어가 사전 단어에 두 번 이상 나타나는 경우 경로에 하나의 노드 만 넣습니다.

#include<iostream>
#include<fstream>
#include<vector>
#include<map>
#include<algorithm>
#include<utility>
#include<cmath>

using namespace std;

const double RESOLUTION = 3.2;

double dist(const pair<double, double>& a, const pair<double, double>& b) {
    return sqrt((a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second));
}

double helper(const vector<pair<double, double> >& a,
        const vector<pair<double, double> >& b,
        vector<vector<double> >& dp,
        int i,
        int j) {
    if (dp[i][j] > -1)
        return dp[i][j];
    else if (i == 0 && j == 0)
        dp[i][j] = dist(a[0], b[0]);
    else if (i > 0 && j == 0)
        dp[i][j] = helper(a, b, dp, i - 1, 0) +
                   dist(a[i], b[0]);
    else if (i == 0 && j > 0)
        dp[i][j] = helper(a, b, dp, 0, j - 1) +
                   dist(a[0], b[j]);
    else if (i > 0 && j > 0)
        dp[i][j] = min(min(helper(a, b, dp, i - 1, j),
                           helper(a, b, dp, i - 1, j - 1)),
                       helper(a, b, dp, i, j - 1)) +
                   dist(a[i], b[j]);
    return dp[i][j];
}

double discretefrechet(const vector<pair<double, double> >& a, const vector<pair<double, double> >& b) {
    vector<vector<double> > dp = vector<vector<double> >(a.size(), vector<double>(b.size(), -1.));
    return helper(a, b, dp, a.size() - 1, b.size() - 1);
}

bool issubsequence(string& a, string& b) {
    // Accounts for repetitions of the same character: hallo subsequence of halo
    int i = 0, j = 0;
    while (i != a.size() && j != b.size()) {
        while (a[i] == b[j])
            ++i;
        ++j;
    }
    return (i == a.size());
}

int main() {
    string swipedword;
    cin >> swipedword;

    ifstream fin;
    fin.open("wordlist");
    map<string, double> candidatedistance = map<string, double>();
    string readword;
    while (fin >> readword) {
        if (issubsequence(readword, swipedword) && readword[0] == swipedword[0] && readword[readword.size() - 1] == swipedword[swipedword.size() - 1]) {
            candidatedistance[readword] = -1.;
        }
    }
    fin.close();

    ifstream fin2;
    fin2.open("keypos.csv");
    map<char, pair<double, double> > keypositions = map<char, pair<double, double> >();
    char rc, comma;
    double rx, ry;
    while (fin2 >> rc >> comma >> rx >> comma >> ry) {
        keypositions[rc] = pair<double, double>(rx, ry);
    }
    fin2.close();

    vector<pair<double, double> > swipedpath = vector<pair<double, double> >();
    for (int i = 0; i != swipedword.size(); ++i) {
        swipedpath.push_back(keypositions[swipedword[i]]);
    }

    for (map<string, double>::iterator thispair = candidatedistance.begin(); thispair != candidatedistance.end(); ++thispair) {
        string thisword = thispair->first;
        vector<pair<double, double> > thispath = vector<pair<double, double> >();
        for (int i = 0; i != thisword.size() - 1; ++i) {
            pair<double, double> linestart = keypositions[thisword[i]];
            pair<double, double> lineend = keypositions[thisword[i + 1]];
            double linelength = dist(linestart, lineend);
            if (thispath.empty() || linestart.first != thispath[thispath.size() - 1].first || linestart.second != thispath[thispath.size() - 1].second)
                thispath.push_back(linestart);
            int segmentnumber = linelength / RESOLUTION;
            for (int j = 1; j < segmentnumber; ++j) {
                thispath.push_back(pair<double, double>(linestart.first + (lineend.first - linestart.first) * ((double)j) / ((double)segmentnumber),
                                    linestart.second + (lineend.second - lineend.second) * ((double)j) / ((double)segmentnumber)));
            }
        }
        pair<double, double> lastpoint = keypositions[thisword[thisword.size() - 1]];
        if (thispath.empty() || lastpoint.first != thispath[thispath.size() - 1].first || lastpoint.second != thispath[thispath.size() - 1].second)
        thispath.push_back(lastpoint);

        thispair->second = discretefrechet(thispath, swipedpath) / (double)(thispath.size());
    }

    double bestscore = 1e9;
    string bestword = "";
    for (map<string, double>::iterator thispair = candidatedistance.begin(); thispair != candidatedistance.end(); ++thispair) {
        double score = thispair->second;
        if (score < bestscore) {
            bestscore = score;
            bestword = thispair->first;
        }
        // cout << thispair->first << ": " << score << endl;
    }
    cout << bestword << endl;

    return 0;
}

clang으로 코드를 컴파일했지만 g++ ./thiscode.cpp -o ./thiscode정상적으로 작동합니다.


1
예! 누군가 마침내 큰 뚱뚱한 알고리즘 이름을 가진 솔루션을 추가했습니다! 귀하의 로그에 있습니다 github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/...
matsjoyce

1
오히려 큰 뚱뚱한 컴퓨터 과학 문제에 대한 간단한 동적 프로그래밍 알고리즘이라고합시다.
camelNeck

어떤 이유로,이의 입력에 실패 할 것 programmijng- 그것은 나를 준다 pang.
Riking

이상하다. 나는 programming그것이 좋아질 것입니다. 줄의 주석을 해제 // cout << thispair->first << ": " << score << endl;하고 결과 점수를 붙여 넣을 수 있습니까?
camelNeck

6

파이썬 2 턴어라운드, 224 226 230 232 230 개 234 패스

이것은 매우 직접적인 접근 방식입니다.

  • 글자의 흐름이 방향 (플러스 시작과 끝)을 바꾸는 지점을 찾으십시오.
  • 이 모든 전환점을 포함하는 가장 긴 단어를 출력하십시오.
import sys, csv, re

wordlistfile = open('wordlist', 'r')
wordlist = wordlistfile.read().split('\n')

layout = 'qwertyuiop asdfghjkl  zxcvbnm'
keypos = dict((l, (2*(i%11)+i/11, i/11)) for i,l in enumerate(layout))

#read input from command line argument
input = sys.argv[1]

#remove repeated letters
input = ''.join(x for i,x in enumerate(input) if i==0 or x!=input[i-1])

#find positions of letters on keyboard
xpos = map(lambda l: keypos[l][0], input)
ypos = map(lambda l: keypos[l][1], input)

#check where the direction changes (neglect slight changes in x, e.g. 'edx')
xpivot = [(t-p)*(n-t)<-1.1 for p,t,n in zip(xpos[:-2], xpos[1:-1], xpos[2:])]
ypivot = [(t-p)*(n-t)<0 for p,t,n in zip(ypos[:-2], ypos[1:-1], ypos[2:])]
pivot = [xp or yp for xp,yp in zip(xpivot, ypivot)]

#the first and last letters are always pivots
pivot = [True] + pivot + [True]

#build regex
regex = ''.join(x + ('+' if p else '*') for x,p in zip(input, pivot))
regexobj = re.compile(regex + '$')

#find all words that match the regex and output the longest one
words = [w for w in wordlist if regexobj.match(w)]
output = max(words, key=len) if words else input
print output

다음으로 실행

python turnarounds.py tyuytrghn

솔루션은 그다지 강력하지 않습니다. 한 번의 잘못된 키 입력으로 실패 할 수 있습니다. 테스트 케이스의 집합이 없기 때문에 그러나, 약간 더 잘못된 철자를 그것은 단지 너무 긴 단어를 선택합니다 경우에 혼동하기, 아주 잘 수행합니다.

편집 : 제공된 키 위치 파일을 사용하지 않고 간단한 레이아웃을 사용하도록 약간 변경했습니다. 파일에서 키 간격이 고르지 않기 때문에 알고리즘이 더 쉬워집니다. 모호한 경우에 일부 임시 타이 브레이커를 포함 시켜서 약간 더 나은 결과를 얻을 수는 있지만이 특정 테스트 세트에 대해 과도하게 최적화됩니다. 실제로 모호하지는 않지만 90도 이상의 방향 변경만을 고려하기 때문에 간단한 알고리즘을 사용하지 않는 일부 실패가 남아 있습니다.


잘 했어! 현재 리더! 로그를 보려면 github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/…
matsjoyce

@ matsjoyce : 내가 찾은 것으로 생각되는 두 가지 맞춤법 실수를 지적하는 질문에 의견을 추가했습니다. :)
Emil

예, 고맙습니다. 다른 확인을하고 있습니다. 그러나 모호한 경우로 무엇을 해야할지 잘 모르겠습니다.
matsjoyce

@matsjoyce : 키보드에서 가능한 다른 경로를 선택하여 일부 모호성을 해결할 수 있습니다. 예는 brats'bgrdsasdrtrds'와 혼동 될 수없는 breasts. 그러나, 그대로 두었다해도 상관 없습니다.
Emil

사실, 그것은 효과가있을 것입니다. 이 세트가 너무 '최적화'되고 최종 점수 세트가 많이 조사되지 않으면 일부 솔루션이 제대로 작동하지 않을 수 있습니다.
matsjoyce

6

PHP 방향 검사기 203 213 216 229 231 229 개 233 패스

이것은 사전에 간단한 패스로 시작하여 입력에 문자가 모두 포함 된 단어 목록을 얻습니다 (Martin이 여기서 한 것 ). 또한 복제 된 경우 l.*대신 확인 만합니다.l.*l.*l

여기서 가장 긴 단어가 변수에 저장됩니다.

그런 다음 스 와이프가 방향을 변경하는 지점의 키 (x 또는 y에서 + / 0에서-또는-/ 0에서 +)를 기준으로 다른 검사가 수행됩니다. 가능한 단어 목록이이 문자 목록과 비교하여 확인되고 일치하지 않는 단어는 제거됩니다. 이 접근 방식은 정확한 스 와이프 회전에 의존합니다.

가장 긴 나머지 단어가 출력되거나, 남아있는 단어가 없으면 이전의 가장 긴 단어가 대신 출력됩니다.

편집 : 확인할 수있는 값으로 방향이 변경되도록 수평선에 모든 문자를 추가했습니다.

PHP 5.4가 필요합니다 (또는 모두 [..]로 대체 array(..)).

<?php
function get_dir($a, $b){
    $c = [0, 0];
    if($a[0] - $b[0] < -1.4) $c[0] = 1;
    else if($a[0] - $b[0] > 1.4) $c[0] = -1;
    if($a[1] < $b[1]) $c[1] = 1;
    else if($a[1] > $b[1]) $c[1] = -1;
    return $c;
}
function load_dict(){
    return explode(PHP_EOL, file_get_contents('wordlist'));
}

$coord = [];
$f = fopen('keypos.csv', 'r');
while(fscanf($f, "%c, %f, %f", $c, $x, $y)){
    $coord[$c] = [$x, $y];  
}
fclose($f);

$dict = load_dict();
$in = $argv[1];
$possib = [];

foreach($dict as $c){
    if($c[0] == $in[0]){
        $q = strlen($c);
        $r = strlen($in);
        $last = '';
        $fail = false;
        $i = $j = 0;
        for($i = 0; $i < $q; ++$i){
            if($last == $c[$i]) continue;
            if($j >= $r){
                $fail = true;
                break;
            }
            while($c[$i] != $in[$j++])
                if($j >= $r){
                    $fail = true; 
                    break;
                }
            if($fail) break;
            $last = $c[$i];
        }
        if(!$fail) 
            $possib[] = $c;
    }
}

$longest = '';
foreach($possib as $p){
    if(strlen($p) > strlen($longest))
        $longest = $p;
}

$dir = [[0, 0]];
$cdir = [0, 0];
$check = '/^' . $in[0] . '.*?';
$olst = []; $p = 1;

$len = strlen($in);
for($i = 1; $i < $len; ++$i){
    $dir[$i] = get_dir($coord[$in[$i - 1]], $coord[$in[$i]]);
    $olddir = $cdir;
    $newdir = $dir[$i];
    $xc = $olddir[0] && $newdir[0] && $newdir[0] != $olddir[0];
    $yc = $olddir[1] && $newdir[1] && $newdir[1] != $olddir[1];
    if($xc || $yc){ // separate dir from current dir
        if($yc && !$xc && $dir[$i - 1][1] == 0){
            $tmp = '';
            for($j = $i - 1; $j >= 0 && $dir[$j][1] == 0; --$j){
                $tmp = '(' . $in[$j-1] . '.*?)?' . $tmp;
            }
            $olst[] = range($p, $p + (($i - 1) - $j));
            $p += ($i - 1) - $j + 1;
            $check .= $tmp . '(' . $in[$i - 1] . '.*?)?';
        }else{
            $check .= $in[$i - 1] . '.*?';
        }
        $cdir = $dir[$i];
    }else{
        if(!$cdir[0]) $cdir[0] = $dir[$i][0]; 
        if(!$cdir[1]) $cdir[1] = $dir[$i][1]; 
    }
}
$check .= substr($in, -1) . '$/';
$olstc = count($olst);
$res = [];
foreach($possib as $p){
    if(preg_match($check, $p, $match)){
        if($olstc){
            $chk = array_fill(0, $olstc, 0);
            for($i = 0; $i < $olstc; ++$i){
                foreach($olst[$i] as $j){
                    if(isset($match[$j]) && $match[$j]){
                        ++$chk[$i];
                    }
                }
                // give extra weight to the last element of a horizontal run
                if(@$match[$olst[$i][count($olst[$i])-1]])
                    $chk[$i] += 0.5;
            }
            if(!in_array(0, $chk)){
                $res[$p] = array_sum($chk);
            }
        }else{
            $res[$p] = 1;
        }
    }
}
$possib = array_keys($res, @max($res));
$newlong = '';
foreach($possib as $p){
    if(strlen($p) > strlen($newlong))
        $newlong = $p;
}
if(strlen($newlong) == 0) echo $longest;
else echo $newlong;

@matsjoyce 질문을 읽는 동안 그 요점을 놓쳤다. 코드는 이제 위치를 사용합니다keypos.csv
es1024

@ es1024 250 개의 테스트 사례에 속하지는 않지만 단어 목록에는 3 개의 연속 문자가있는 238 개의 단어가 포함되어 있습니다 (지금까지 내가 확인한 단어는으로 끝나는 단어 임 sss).
Martin Ender


3

Python 3, 코너 시뮬레이터, 241 및 240 패스

연산:

  • 입력과 단어 목록의 모든 단어를 중복 제거 (동일한 문자의 연속 실행 제거) (사전을 사용하여 원래 단어를 다시 가져옴)
  • 스 와이프의 시작과 끝으로 시작하고 끝나지 않는 단어를 모두 제거하십시오 (첫 번째 패스)
  • 모든 모서리에서 정규 표현식을 80도 이상으로 만든 다음이 단어와 일치하지 않는 단어를 모두 제거하십시오 (두 번째 패스).
  • 각 단어 (예 : Regex Solver)를 스 와이프에 대해 정규식 한 다음 스 와이프를 이론적으로 일련의 직선으로 분할하고 직선을 따라 해당 라인을 따라 움직이는 손가락 ( significant_letter기능) (세 번째 패스) 으로 생성 될 수 있는지 확인합니다.
  • 직선에 근접하여 단어를 정렬
  • 그런 다음 길이를 타이 브레이커로 사용하십시오 (더 길수록 좋습니다).
  • 그런 다음 Levenshtein 거리를 최종 타이 브레이커로 사용하십시오.
  • 출력 단어!

암호:

# Corner Sim

from math import atan, degrees, pi, factorial, cos, radians
import csv
import re
import sys

keys = {}
key_size = 1.5
for line in open("keypos.csv"):
    k, x, y = map(str.strip, line.split(","))
    keys[k] = float(x), float(y)


def deduplicate(s):
    return s[0] + "".join(s[i + 1] for i in range(len(s) - 1) if s[i + 1] != s[i])


def angle(coord1, coord2):
    x1, y1, x2, y2 = coord1 + coord2
    dx, dy = x2 - x1, y1 - y2
    t = abs(atan(dx/dy)) if dy else pi / 2
    if dx >= 0 and dy >= 0: a = t
    elif dx >= 0 and dy < 0: a = pi - t
    elif dx < 0 and dy >= 0: a = 2 * pi - t
    else: a = t + pi
    return degrees(a)


def significant_letter(swipe):
    if len(swipe) <= 2: return 0
    x1, y1, x2, y2 = keys[swipe[0]] + keys[swipe[-1]]
    m = 0 if x2 == x1 else (y2 - y1) / (x2 - x1)
    y = lambda x: m * (x - x1) + y1
    def sim_fun(x2, y2):
        try: return (x2 / m + y2 - y1 + x1 * m) / (m + 1 / m)
        except ZeroDivisionError: return x2
    dists = []
    for i, key in enumerate(swipe[1:-1]):
        sx, bx = min(x1, x2), max(x1, x2)
        pos_x = max(min(sim_fun(*keys[key]), bx), sx)
        sy, by = min(y1, y2), max(y1, y2)
        pos_y = max(min(y(pos_x), by), sy)
        keyx, keyy = keys[key]
        dist = ((keyx - pos_x) ** 2 + (keyy - pos_y) ** 2) ** 0.5
        a = angle((keyx, keyy), (pos_x, pos_y))
        if 90 <= a <= 180: a = 180 - a
        elif 180 <= a <= 270: a = a - 180
        elif 270 <= a <= 360: a = 360 - a
        if a > 45: a = 90 - a
        h = key_size / 2 / cos(radians(a))
        dists.append((max(dist - h, 0), i + 1, key))
    return sorted(dists, reverse=True)[0][0]


def closeness2(s, t):
    # https://en.wikipedia.org/wiki/Levenshtein_distance
    if s == t: return 0
    if not len(s): return len(t)
    if not len(t): return len(s)
    v0 = list(range(len(t) + 1))
    v1 = list(range(len(t) + 1))
    for i in range(len(s)):
        v1[0] = i + 1
        for j in range(len(t)):
            cost = 0 if s[i] == t[j] else 1
            v1[j + 1] = min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost)
        for j in range(len(v0)):
            v0[j] = v1[j]
    return v1[len(t)] / len(t)


def possible(swipe, w, s=False):
    m = re.match("^" + "(.*)".join("({})".format(i) for i in w) + "$", swipe)
    if not m or s:
        return bool(m)
    return closeness1(swipe, w) < 0.8


def closeness1(swipe, w):
    m = re.match("^" + "(.*)".join("({})".format(i) for i in w) + "$", swipe)
    unsigpatches = []
    groups = m.groups()
    for i in range(1, len(groups), 2):
        unsigpatches.append(groups[i - 1] + groups[i] + groups[i + 1])
    if debug: print(unsigpatches)
    sig = max(map(significant_letter, unsigpatches))
    if debug: print(sig)
    return sig


def find_closest(swipes):
    level1, level2, level3, level4 = swipes
    if debug: print("Loading words...")
    words = {deduplicate(i.lower()): i.lower() for i in open("wordlist").read().split("\n") if i[0] == level1[0] and i[-1] == level1[-1]}
    if debug: print("Done first filter (start and end):", words)
    r = re.compile("^" + ".*".join(level4) + "$")
    pos_words2 = list(filter(lambda x: bool(r.match(x)), words))
    if debug: print("Done second filter (sharpest points):", pos_words2)
    pos_words = pos_words2 or words
    pos_words = list(filter(lambda x: possible(level1, x), pos_words)) or list(filter(lambda x: possible(level1, x, s=True), pos_words))
    if debug: print("Done third filter (word regex):", pos_words)
    sorted_pos_words = sorted((closeness1(level1, x), -len(x), closeness2(level1, x), x)
                              for x in pos_words)
    if debug: print("Closeness matching (to", level2 + "):", sorted_pos_words)
    return words[sorted_pos_words[0][3]]


def get_corners(swipe):
    corners = [[swipe[0]] for i in range(4)]
    last_dir = last_char = None
    for i in range(len(swipe) - 1):
        dir = angle(keys[swipe[i]], keys[swipe[i + 1]])
        if last_dir is not None:
            d = abs(last_dir - dir)
            a_diff = min(360 - d, d)
            corners[0].append(last_char)
            if debug: print(swipe[i], dir, last_dir, a_diff, end=" ")
            if a_diff >= 55:
                if debug: print("C", end=" ")
                corners[1].append(last_char)
            if a_diff >= 65:
                if debug: print("M", end=" ")
                corners[2].append(last_char)
            if a_diff >= 80:
                if debug: print("S", end=" ")
                corners[3].append(last_char)
            if debug: print()
        last_dir, last_char = dir, swipe[i + 1]
    tostr = lambda x: deduplicate("".join(x + [swipe[-1]]).lower())
    return list(map(tostr, corners))


if __name__ == "__main__":
    debug = "-d" in sys.argv
    if debug: sys.argv.remove("-d")
    swipe = deduplicate(sys.argv[1] if len(sys.argv) > 1 else input()).lower()
    corners = get_corners(swipe)
    if debug: print(corners)
    print(find_closest(corners))

로 실행 python3 entries/corner_sim.py


이것은 정답입니다. 내 대답을 할 필요가 없습니다.
최적화

@Optimizer 글쎄, 메타 토론은 당신의 대답을 받아들이는 것을 선호하는 것 같습니다.
matsjoyce

메타 토론 만 읽은 후에 나는 당신의 결정에 괜찮 았지만 이것도 좋습니다 () :)
Optimizer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.