スワイプタイプコンバーター


27

ノートパソコンでのタイピングの次の革命は、SwiftKeyによって2014年4月1日にリリースされました。しかし、私は最初にスワイプするナノクローンを書く人になりたいのですが、スワイプテキストからリアルテキストライブラリへの良いスワイプを見つけることができず、それらを待つことができないので、私はここで尋ねています。

仕事

スワイプテキストを取り込んで、同等の実テキストを出力するプログラムを作成します。例:

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でスワイプされた「単語」を1つ受け取ります。
  • スワイプ入力の最初と最後の文字は、実際の単語の最初と最後の文字と同じです
  • ユーザーが合理的に直線を作成すると想定できますが、サンプルデータを使用してこれを確認できます(サンプルデータを作成し、最終テストデータを作成します)
  • 入力があいまいな場合は、どちらかの出力を選択できますが、テストデータからすべてのあいまいさを排除しようとします
  • この単語はこの単語リストに含まれます(ただし、スワイプされます)。単語リストは現在のディレクトリにあり、読み取ることができます(改行区切り、名前付きwordlist、拡張子なし)。
  • スワイプには小文字のアルファベットのみが含まれます
  • ユーザーがキーで一時停止すると、スワイプに重複した文字が含まれる場合があります
  • プログラムは標準出力に出力する必要があります(大文字と小文字は区別されません)
  • プログラム0は戻りコードとして戻らなければなりません
  • 実行コマンド、コンパイルコマンド(必要な場合)、名前、使用する入力パスを指定する必要があります
  • 標準的な抜け穴が適用されます(ただし、助けにはならない場合があります)
  • 非組み込みライブラリは許可されていません
  • 確定的、非ゴルフ/難読化ソリューションが望ましい
  • ファイルの書き込み、ネットワーキングなどはありません
  • コードは1秒以内に実行する必要があります(コードは単語ごとに1回実行されます)
  • スコアリングの実行は、4つの仮想コード(2つの実際のコード)を備えたIntel i7 Haswellプロセッサーで実行されるため、必要な場合はスレッドを使用できます。
  • 5000バイトの最大コード長
  • 使用する言語には、Linux(Arch 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

みんなとhgfdsasdertyuiopoiuuy swertyuiopoijnhgによくやった!


「解決策」とは何ですか?そのコードはどこにありますか?
ドアノブ



@Optimizerわからない他の例について、しかし" P oiuytres A SE R ES A S D fghui O iugfd X CGU UG C XS A sdfghjk リットルyは "を除いて、順番に、 "逆説"のすべての文字が含まれていますl、倍になりません。
es1024 14年

1
@Optimiserさて、あなたの提出はそれを打ち負かすだろうと思ったが、それはすぐ下だった(少し調整することでそれが変わったと思う)。私はそれを受け入れることができるようです、それで...私は(私がそれを受け入れることから担当者を得るように見えません)?私は他の誰かを受け入れたいのですが、それはルールに従っていません(あなたが良い考えを持っていない限り)。
matsjoyce 14年

回答:


12

JavaScript、ES6、Three Pass Optimizer、112 187 235 240 241 243および231 234パス

最初にキーストロークシーケンスの重要なキーを計算し、次に3つのフィルターにシーケンスを渡す3パスフィルター:

  1. 重要なキーと支援キーから緩やかに形成されたRegEx。これにより、ほとんどのキー(約150)に対して正しい結果が得られます。
  2. 重要なキーだけで作られた厳格な正規表現。これにより、余分な85シーケンスに対して正しい結果が得られます
  3. 厳密な回答のあいまいさを把握するための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

それは問題ありませんが、スワイプはキーボードの角度で行われますので、あなたの選択です(ただし、スコアに影響はないようです!)
matsjoyce


240パスは抜群です!あいまいさがそのような良い結果を妨げると思いました。これが最終テストセットでどのように機能するか興味があります。
エミール14年

@エミール-ええ、私もそれを待っています。
オプティマイザー14年

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 14年

私はこれのような隣同士に二つの同一の文字、とすべての正しい言葉を投げるかもしれないと思うparadoxicallyと、l正規表現で要求として二回しか入力で一度表示され、よりむしろでしょう。
es1024 14年

@ es1024、ああ、ありがとう、サンドボックスでこのアルゴリズムを最初に提案したとき、私は実際にそれを知っていましたが、昨日それを忘れていました。後で修正します。
マーティンエンダー

7

C ++、離散フレシェ距離-201 220 222 232および232パス

私にとって、この問題は、残念ながら計算が非常に難しいフレシェ距離を非常に要求していました。

楽しみのために、コンピューティング離散フレシェ距離(1994)でThomas EiterとHeikki Mannilaによって記述された離散近似を実装することで問題にアプローチしようとしました。

最初は、入力のサブシーケンスであるリスト内のすべての単語をフィルタリングするために、他のアプローチと同じアプローチを使用しています(同じ文字の複数の連続した出現も考慮します)。次に、すべての単語の文字から文字への多角形曲線を中間点で埋め、入力曲線と照合します。最後に、すべての距離を単語の長さで割り、最小スコアを取得します。

これまでのところ、この方法は私が期待していたほどうまく機能しませんが(コード例を「chide」として認識します)、これはまだ見つかっていないバグの結果である可能性があります。そうでなければ、フレシェ距離の他のバリエーションを使用することも考えられます(「犬の最大鎖長」ではなく「平均」)。

編集:今、私は「平均犬の鎖の長さ」への近似値を使用しています。これは、すべての距離の合計を最小化し、後でそれを距離の数で除算する、両方のパス間の順序マッピングを見つけていることを意味します。

辞書の単語に同じ文字が2回以上出現する場合、パスにノードを1つだけ入れます。

#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 14年

なんらかの理由で、これはの入力に対して失敗するようですprogrammijng-それは私に与えますpang
ライキング14年

それは奇妙です。私はprogrammingそれがあるべきようになっています。行のコメントを解除して// cout << thispair->first << ": " << score << endl;、結果のスコアを貼り付けてください。
camelNeck 14年

6

Python 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

ソリューションは非常に堅牢ではありません。間違ったキーストロークを1つ入力すると、失敗します。ただし、テストケースのセットにはスペルミスがほとんどないため、非常にうまく機能し、長すぎる単語を選択する場合にのみ混乱します。

編集:提供されたキーポジションファイルを使用せず、レイアウトを簡素化するために少し変更しました。これにより、ファイル内のキーの間隔が均等にならないため、アルゴリズムが簡単になります。あいまいな場合にアドホックタイブレーカーを含めることで、わずかに良い結果を得ることができますが、この特定のテストセットでは最適化が過剰になります。実際には曖昧ではないものもありますが、90度を超える方向の変化のみを考慮しているため、単純なアルゴリズムでは検出できません。


よくやった!現在のリーダー!ログを表示する場合は、github.com
matsjoyce /

@matsjoyce:私が見つけたと思う2つのスペルミスを指摘する質問にコメントを追加しました。:)
エミール

はい、ありがとう、私はちょうどそれをもう一度チェックしています。ただし、あいまいな場合の対処方法はわかりません。
matsjoyce 14年

@matsjoyce:キーボード上の別の可能なパスを選択することで、いくつかのあいまいさを解決できます。例えば、brats可能性'bgrdsasdrtrds'と混同することができませんbreasts。ただし、そのままにしておいても構いません。
エミール

確かに、それはうまくいくでしょう。私はちょうどこのセットは、あまりにも「最適」作られ、最終的なスコアリングセットが非常に精査まで置かれていない場合は、いくつかの解決策が同様に行わなくてもよいことを心配している
matsjoyce

6

PHP、Direction Checker、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 14年

@ es1024 250個のテストケースの一部ではありませんが、単語リストには3つの連続した文字を持つ238個の単語が含まれています(これまでに確認したのはで終わる単語のみですsss)-重複排除でそれらが検出されるとは思いません。
マーティンエンダー

必要な場合は、github.com
matsjoyce /

3

Python 3、コーナーシミュレーター、241および240パス

アルゴリズム:

  • 入力と単語リスト内のすべての単語を重複排除(同じ文字の連続した実行を削除)(辞書を使用して元の単語を取得)
  • スワイプの開始と終了で開始および終了しないすべての単語を削除します(最初のパス)
  • 80度を超えるすべてのコーナーから正規表現を作成し、これに一致しないすべての単語を削除します(2回目のパス)
  • 各単語(Regexソルバーなど)をスワイプに対して正規表現し、スワイプを一連の理論的に直線に分割し、それらが直線であり、その線に沿って移動する指によって生成された可能性があるかどうかを確認します(significant_letter関数)(3回目のパス)
  • 直線に近い順に単語を並べ替えます
  • 次に、長さをタイブレーカーとして使用します(長い方が良い)
  • 次に、最終的なタイブレーカーとしてレーベンシュタイン距離を使用します
  • 出力ワード!

コード:

# 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


これは有効な答えでした。私の答えにする必要はありません。
オプティマイザー14年

@Optimizerさて、メタディスカッションはあなたの答えを受け入れることを好むようですので、私はそれでいいのです。
matsjoyce 14年

そのメタディスカッションのみを読んだ後、私はあなたの決定に大丈夫でしたが、これも良い(良い)です:)
オプティマイザー14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.