it-swarm.com.de

Wie teile ich Text ohne Leerzeichen in Wortlisten auf?

Eingabe:"tableapplechairtablecupboard..." Viele Wörter

Was wäre ein effizienter Algorithmus, um solchen Text in die Liste der Wörter aufzuteilen und Folgendes zu erhalten:

Ausgabe:["table", "Apple", "chair", "table", ["cupboard", ["cup", "board"]], ...]

Als erstes müssen Sie alle möglichen Wörter (beginnend mit dem ersten Buchstaben) durchgehen und das längste mögliche Wort finden. Fahren Sie mit position=Word_position+len(Word) fort.

P.S.
Wir haben eine Liste aller möglichen Wörter.
Wort "Schrank" kann "Tasse" und "Brett" sein, am längsten auswählen.
Sprache: Python, aber Hauptsache ist der Algorithmus selbst.

89
Sergey

Ein naiver Algorithmus liefert keine guten Ergebnisse, wenn er auf reale Daten angewendet wird. Hier ist ein 20-zeiliger Algorithmus, der die relative Häufigkeit von Wörtern ausnutzt, um genaue Ergebnisse für echten Text zu erhalten.

(Wenn Sie eine Antwort auf Ihre ursprüngliche Frage wünschen, bei der keine Worthäufigkeit verwendet wird, müssen Sie präzisieren, was genau unter "längstes Wort" zu verstehen ist. Ist es besser, ein Wort mit 20 Buchstaben und zehn Buchstaben mit 3 Buchstaben zu haben? Wenn Sie sich für eine genaue Definition entschieden haben, müssen Sie nur die Zeile ändern, in der wordcost definiert ist, um die beabsichtigte Bedeutung wiederzugeben.)

Die Idee

Der beste Weg, um fortzufahren, ist model die Verteilung der Ausgabe. Eine gute erste Annäherung ist die Annahme, dass alle Wörter unabhängig voneinander verteilt sind. Dann müssen Sie nur die relative Häufigkeit aller Wörter kennen. Es ist vernünftig anzunehmen, dass sie dem Zipfschen Gesetz folgen, dh das Wort mit Rang n in der Liste der Wörter hat eine Wahrscheinlichkeit von ungefähr 1/(n log N ) wobei N die Anzahl der Wörter im Wörterbuch ist.

Nachdem Sie das Modell repariert haben, können Sie mithilfe der dynamischen Programmierung auf die Position der Räume schließen. Der wahrscheinlichste Satz ist derjenige, der das Produkt der Wahrscheinlichkeit jedes einzelnen Wortes maximiert, und es ist einfach, ihn mit dynamischer Programmierung zu berechnen. Anstatt die Wahrscheinlichkeit direkt zu verwenden, verwenden wir einen als Logarithmus der Umkehrung der Wahrscheinlichkeit definierten Kosten, um Überläufe zu vermeiden.

Der Code

from math import log

# Build a cost dictionary, assuming Zipf's law and cost = -math.log(probability).
words = open("words-by-frequency.txt").read().split()
wordcost = dict((k, log((i+1)*log(len(words)))) for i,k in enumerate(words))
maxword = max(len(x) for x in words)

def infer_spaces(s):
    """Uses dynamic programming to infer the location of spaces in a string
    without spaces."""

    # Find the best match for the i first characters, assuming cost has
    # been built for the i-1 first characters.
    # Returns a pair (match_cost, match_length).
    def best_match(i):
        candidates = enumerate(reversed(cost[max(0, i-maxword):i]))
        return min((c + wordcost.get(s[i-k-1:i], 9e999), k+1) for k,c in candidates)

    # Build the cost array.
    cost = [0]
    for i in range(1,len(s)+1):
        c,k = best_match(i)
        cost.append(c)

    # Backtrack to recover the minimal-cost string.
    out = []
    i = len(s)
    while i>0:
        c,k = best_match(i)
        assert c == cost[i]
        out.append(s[i-k:i])
        i -= k

    return " ".join(reversed(out))

die du mit benutzen kannst

s = 'thumbgreenappleactiveassignmentweeklymetaphor'
print(infer_spaces(s))

Die Ergebnisse

Ich verwende dieses schnelle und schmutzige Wörterbuch mit 125.000 Wörtern, das ich zusammengestellt habe aus einer kleinen Untergruppe von Wikipedia.

Vorher: thumbgreenappleactiveassignmentweeklymetaphor.
Nach: Daumen grün Apple aktive Zuordnung wöchentliche Metapher.

Vorher: gibt es Textinformationen für die Völker, die aus dem ml-Text herausgelöst werden, aber die angegebenen Zeichen sind in Beispielbeispielen enthalten.

Nach: gibt es massenweise Textinformationen von Kommentaren der Leute, die aus HTML analysiert werden, aber keine begrenzten Zeichen enthalten, zum Beispiel daumengrün Apple aktive Zuordnung wöchentliche Metapher anscheinend gibt es daumengrün Apple usw. Ich habe auch ein großes Wörterbuch, um zu fragen, ob das Wort vernünftig ist, also was ist der schnellste Weg der Extraktion vielen Dank.

Vorher: itwasadarkandstormynighttherainfellintorrentsexceptatoccasionalintervalswhenitwascheckedbyaviolentgustofwindwhichsweptupthestreetsforitisinlondonthatoursceneliesrattlingalongthehousetopsandfiercelyagitatingthescantyflameofthelampsthatstruggledagainstthedarkness.

Nach: einer dunklen und stürmischen Nacht fiel der Regen in Strömen, außer in gelegentlichen Abständen, als er von einem heftigen Windstoß, der die Küste hinauffegte, kontrolliert wurde In den Straßen von London rasselt unsere Szene an den Hausdächern entlang und bewegt heftig die spärliche Flamme der Lampen, die sich gegen die Dunkelheit wehrten.

Wie Sie sehen können, ist es im Wesentlichen einwandfrei. Der wichtigste Teil ist, sicherzustellen, dass Ihre Wortliste auf einen Korpus trainiert wurde, der dem entspricht, auf den Sie tatsächlich stoßen werden. Andernfalls sind die Ergebnisse sehr schlecht.


Optimierung

Die Implementierung benötigt eine lineare Menge an Zeit und Speicher, sodass sie einigermaßen effizient ist. Wenn Sie weitere Beschleunigungen benötigen, können Sie einen Suffixbaum aus der Word-Liste erstellen, um die Größe der Kandidatenmenge zu verringern.

Wenn Sie eine sehr große fortlaufende Zeichenfolge verarbeiten müssen, ist es sinnvoll, die Zeichenfolge zu teilen, um eine übermäßige Speichernutzung zu vermeiden. Beispielsweise könnten Sie den Text in Blöcken von 10000 Zeichen plus einem Rand von 1000 Zeichen auf beiden Seiten verarbeiten, um Randeffekte zu vermeiden. Dadurch wird der Speicherverbrauch auf ein Minimum reduziert und die Qualität wird mit ziemlicher Sicherheit nicht beeinträchtigt.

158
Generic Human

Basierend auf der hervorragenden Arbeit in beste Antwort habe ich ein pip -Paket für die einfache Verwendung erstellt.

>>> import wordninja
>>> wordninja.split('derekanderson')
['derek', 'anderson']

Führen Sie zum Installieren pip install wordninja.

Die einzigen Unterschiede sind gering. Dies gibt ein list anstatt eines str zurück, es funktioniert in python3, enthält die Wortliste und wird ordnungsgemäß aufgeteilt, auch wenn nicht-alphanumerische Zeichen (wie Unterstriche, Bindestriche usw.) vorhanden sind.

Nochmals vielen Dank an Generic Human!

https://github.com/keredson/wordninja

35
keredson

Hier ist die Lösung mit rekursiver Suche:

def find_words(instring, prefix = '', words = None):
    if not instring:
        return []
    if words is None:
        words = set()
        with open('/usr/share/dict/words') as f:
            for line in f:
                words.add(line.strip())
    if (not prefix) and (instring in words):
        return [instring]
    prefix, suffix = prefix + instring[0], instring[1:]
    solutions = []
    # Case 1: prefix in solution
    if prefix in words:
        try:
            solutions.append([prefix] + find_words(suffix, '', words))
        except ValueError:
            pass
    # Case 2: prefix not in solution
    try:
        solutions.append(find_words(suffix, prefix, words))
    except ValueError:
        pass
    if solutions:
        return sorted(solutions,
                      key = lambda solution: [len(Word) for Word in solution],
                      reverse = True)[0]
    else:
        raise ValueError('no solution')

print(find_words('tableapplechairtablecupboard'))
print(find_words('tableprechaun', words = set(['tab', 'table', 'leprechaun'])))

ausbeuten

['table', 'Apple', 'chair', 'table', 'cupboard']
['tab', 'leprechaun']
16
unutbu

Mit einem trieDatenstruktur , das die Liste der möglichen Wörter enthält, wäre es nicht zu kompliziert, Folgendes zu tun:

  1. Vorwärtszeiger (in der verketteten Zeichenfolge)
  2. Suchen und speichern Sie den entsprechenden Knoten im Trie
  3. Wenn der Testknoten untergeordnete Elemente hat (z. B. längere Wörter), fahren Sie mit 1 fort.
  4. Wenn der erreichte Knoten keine Kinder hat, ist die längste Wortübereinstimmung aufgetreten. Fügen Sie das Wort (im Knoten gespeichert oder nur während des Durchlaufs verkettet) zur Ergebnisliste hinzu, setzen Sie den Zeiger im Trie zurück (oder setzen Sie die Referenz zurück) und beginnen Sie von vorne
10
miku

Die Lösung von Unutbu war ziemlich nah, aber ich finde den Code schwer zu lesen und er brachte nicht das erwartete Ergebnis. Die generische Lösung von Human hat den Nachteil, dass Word-Frequenzen benötigt werden. Nicht für alle Anwendungsfälle geeignet.

Hier ist eine einfache Lösung mit einem Divide and Conquer-Algorithmus .

  1. Es wird versucht, die Anzahl der Wörter zu minimieren . find_words('cupboard') gibt ['cupboard'] und nicht ['cup', 'board'] zurück (vorausgesetzt, cupboard, cup und board befinden sich im Wörterbuch )
  2. Die optimale Lösung ist nicht eindeutig , die Implementierung unten gibt eine Lösung zurück. find_words('charactersin') könnte ['characters', 'in'] oder ['character', 'sin'] zurückgeben (siehe unten). Sie können den Algorithmus ganz einfach ändern, um alle optimalen Lösungen zurückzugeben.
  3. In dieser Implementierung sind die Lösungen auswendig gelernt so, dass sie in angemessener Zeit ausgeführt werden.

Der Code:

words = set()
with open('/usr/share/dict/words') as f:
    for line in f:
        words.add(line.strip())

solutions = {}
def find_words(instring):
    # First check if instring is in the dictionnary
    if instring in words:
        return [instring]
    # No... But maybe it's a result we already computed
    if instring in solutions:
        return solutions[instring]
    # Nope. Try to split the string at all position to recursively search for results
    best_solution = None
    for i in range(1, len(instring) - 1):
        part1 = find_words(instring[:i])
        part2 = find_words(instring[i:])
        # Both parts MUST have a solution
        if part1 is None or part2 is None:
            continue
        solution = part1 + part2
        # Is the solution found "better" than the previous one?
        if best_solution is None or len(solution) < len(best_solution):
            best_solution = solution
    # Remember (memoize) this solution to avoid having to recompute it
    solutions[instring] = best_solution
    return best_solution

Dies dauert auf meinem 3GHz-Rechner ungefähr 5 Sekunden:

result = find_words("thereismassesoftextinformationofpeoplescommentswhichisparsedfromhtmlbuttherearenodelimitedcharactersinthemforexamplethumbgreenappleactiveassignmentweeklymetaphorapparentlytherearethumbgreenappleetcinthestringialsohavealargedictionarytoquerywhetherthewordisreasonablesowhatsthefastestwayofextractionthxalot")
assert(result is not None)
print ' '.join(result)

die Reis Massen von Textinformationen der Leute Kommentare, die von HTML analysiert werden, aber es gibt keine begrenzten Zeichen, zum Beispiel daumengrün Apple aktive Zuordnung wöchentliche Metapher, anscheinend gibt es daumengrün Apple etc in der Zeichenfolge habe ich auch ein großes Wörterbuch, um zu fragen, ob das Wort vernünftig ist, so was der schnellste Weg der Extraktion thxa Menge ist

9
Rems

Die Antwort von https://stackoverflow.com/users/1515832/generic-human ist großartig. Die beste Implementierung, die ich je gesehen habe, war Peter Norvig selbst in seinem Buch 'Beautiful Data'.

Bevor ich seinen Code einfüge, möchte ich erläutern, warum die Methode von Norvig genauer ist (obwohl sie im Code etwas langsamer und länger ist).

1) Die Daten sind etwas besser - sowohl in Bezug auf die Größe als auch in Bezug auf die Genauigkeit (er verwendet eine Wortanzahl anstelle einer einfachen Rangfolge) 2) Noch wichtiger ist, dass die Logik hinter n-Gramm den Ansatz wirklich so genau macht .

Das Beispiel, das er in seinem Buch liefert, ist das Problem, eine Zeichenfolge 'Sitdown' aufzuteilen. Nun würde eine Nicht-Bigram-Methode zum Teilen von Strings p ('sit') * p ('down') berücksichtigen, und wenn dies weniger ist als das p ('sitdown') - was ziemlich oft der Fall sein wird - wird es NICHT geteilt es, aber wir würden es wollen (die meiste Zeit).

Wenn Sie jedoch das Bigram-Modell haben, können Sie p ('hinsetzen') als Bigram gegen p ('hinsetzen') bewerten und ersteres gewinnt. Wenn Sie keine Bigramme verwenden, wird die Wahrscheinlichkeit, dass die Wörter, die Sie aufteilen, als unabhängig behandelt. Dies ist jedoch nicht der Fall. Einige Wörter werden eher nacheinander angezeigt. Leider sind dies auch die Wörter, die oft in vielen Fällen zusammenkleben und den Splitter verwirren.

Hier ist der Link zu den Daten (es handelt sich um Daten für 3 verschiedene Probleme und nur um eine Segmentierung. Bitte lesen Sie das Kapitel für Details): http://norvig.com/ngrams/

und hier ist der Link zum Code: http://norvig.com/ngrams/ngrams.py

Diese Links sind schon eine Weile in Betrieb, aber ich kopiere und füge den Segmentierungsteil des Codes trotzdem hier ein

import re, string, random, glob, operator, heapq
from collections import defaultdict
from math import log10

def memo(f):
    "Memoize function f."
    table = {}
    def fmemo(*args):
        if args not in table:
            table[args] = f(*args)
        return table[args]
    fmemo.memo = table
    return fmemo

def test(verbose=None):
    """Run some tests, taken from the chapter.
    Since the hillclimbing algorithm is randomized, some tests may fail."""
    import doctest
    print 'Running tests...'
    doctest.testfile('ngrams-test.txt', verbose=verbose)

################ Word Segmentation (p. 223)

@memo
def segment(text):
    "Return a list of words that is the best segmentation of text."
    if not text: return []
    candidates = ([first]+segment(rem) for first,rem in splits(text))
    return max(candidates, key=Pwords)

def splits(text, L=20):
    "Return a list of all possible (first, rem) pairs, len(first)<=L."
    return [(text[:i+1], text[i+1:]) 
            for i in range(min(len(text), L))]

def Pwords(words): 
    "The Naive Bayes probability of a sequence of words."
    return product(Pw(w) for w in words)

#### Support functions (p. 224)

def product(nums):
    "Return the product of a sequence of numbers."
    return reduce(operator.mul, nums, 1)

class Pdist(dict):
    "A probability distribution estimated from counts in datafile."
    def __init__(self, data=[], N=None, missingfn=None):
        for key,count in data:
            self[key] = self.get(key, 0) + int(count)
        self.N = float(N or sum(self.itervalues()))
        self.missingfn = missingfn or (lambda k, N: 1./N)
    def __call__(self, key): 
        if key in self: return self[key]/self.N  
        else: return self.missingfn(key, self.N)

def datafile(name, sep='\t'):
    "Read key,value pairs from file."
    for line in file(name):
        yield line.split(sep)

def avoid_long_words(key, N):
    "Estimate the probability of an unknown Word."
    return 10./(N * 10**len(key))

N = 1024908267229 ## Number of tokens

Pw  = Pdist(datafile('count_1w.txt'), N, avoid_long_words)

#### segment2: second version, with bigram counts, (p. 226-227)

def cPw(Word, prev):
    "Conditional probability of Word, given previous Word."
    try:
        return P2w[prev + ' ' + Word]/float(Pw[prev])
    except KeyError:
        return Pw(Word)

P2w = Pdist(datafile('count_2w.txt'), N)

@memo 
def segment2(text, prev='<S>'): 
    "Return (log P(words), words), where words is the best segmentation." 
    if not text: return 0.0, [] 
    candidates = [combine(log10(cPw(first, prev)), first, segment2(rem, first)) 
                  for first,rem in splits(text)] 
    return max(candidates) 

def combine(Pfirst, first, (Prem, rem)): 
    "Combine first and rem results into one (probability, words) pair." 
    return Pfirst+Prem, [first]+rem 

Wenn Sie die Wortliste in ein DFA (das sehr langsam sein wird) vorkompilieren, ist die Zeit, die zum Anpassen einer Eingabe benötigt wird, proportional zur Länge des Zeichenfolge (in der Tat nur ein wenig langsamer als nur über die Zeichenfolge iterieren).

Dies ist effektiv eine allgemeinere Version des oben erwähnten Algorithmus. Ich erwähne es nur für die Vollständigkeit - bis jetzt gibt es keine DFA-Implementierung, die Sie nur verwenden können. RE2 würde funktionieren, aber ich weiß nicht, ob Sie mit den Python) -Bindungen die Größe eines DFA bestimmen können, bevor er die kompilierten DFA-Daten einfach wegwirft und sucht NFA.

2

Hier ist die akzeptierte Antwort in JavaScript übersetzt (erfordert node.js und die Datei "wordninja_words.txt" von https://github.com/keredson/wordninja ):

var fs = require("fs");

var splitRegex = new RegExp("[^a-zA-Z0-9']+", "g");
var maxWordLen = 0;
var wordCost = {};

fs.readFile("./wordninja_words.txt", 'utf8', function(err, data) {
    if (err) {
        throw err;
    }
    var words = data.split('\n');
    words.forEach(function(Word, index) {
        wordCost[Word] = Math.log((index + 1) * Math.log(words.length));
    })
    words.forEach(function(Word) {
        if (Word.length > maxWordLen)
            maxWordLen = Word.length;
    });
    console.log(maxWordLen)
    splitRegex = new RegExp("[^a-zA-Z0-9']+", "g");
    console.log(split(process.argv[2]));
});


function split(s) {
    var list = [];
    s.split(splitRegex).forEach(function(sub) {
        _split(sub).forEach(function(Word) {
            list.Push(Word);
        })
    })
    return list;
}
module.exports = split;


function _split(s) {
    var cost = [0];

    function best_match(i) {
        var candidates = cost.slice(Math.max(0, i - maxWordLen), i).reverse();
        var minPair = [Number.MAX_SAFE_INTEGER, 0];
        candidates.forEach(function(c, k) {
            if (wordCost[s.substring(i - k - 1, i).toLowerCase()]) {
                var ccost = c + wordCost[s.substring(i - k - 1, i).toLowerCase()];
            } else {
                var ccost = Number.MAX_SAFE_INTEGER;
            }
            if (ccost < minPair[0]) {
                minPair = [ccost, k + 1];
            }
        })
        return minPair;
    }

    for (var i = 1; i < s.length + 1; i++) {
        cost.Push(best_match(i)[0]);
    }

    var out = [];
    i = s.length;
    while (i > 0) {
        var c = best_match(i)[0];
        var k = best_match(i)[1];
        if (c == cost[i])
            console.log("Alert: " + c);

        var newToken = true;
        if (s.slice(i - k, i) != "'") {
            if (out.length > 0) {
                if (out[-1] == "'s" || (Number.isInteger(s[i - 1]) && Number.isInteger(out[-1][0]))) {
                    out[-1] = s.slice(i - k, i) + out[-1];
                    newToken = false;
                }
            }
        }

        if (newToken) {
            out.Push(s.slice(i - k, i))
        }

        i -= k

    }
    return out.reverse();
}
1

Wenn Sie eine vollständige Liste der in der Zeichenfolge enthaltenen Wörter haben:

Word_list = ["table", "Apple", "chair", "cupboard"]

Verwenden des Listenverständnisses zum Durchlaufen der Liste, um das Wort zu lokalisieren und wie oft es angezeigt wird.

string = "tableapplechairtablecupboard"

def split_string(string, Word_list):

    return ("".join([(item + " ")*string.count(item.lower()) for item in Word_list if item.lower() in string])).strip()


Die Funktion liefert eine string Ausgabe von Wörtern in der Reihenfolge der Liste table table Apple chair cupboard

0
zainsharif1

Das Erweitern des Vorschlags @ mikus , einen Trie zu verwenden, und des Nur-Anhängen-Vorschlags Trie ist relativ einfach in python:

class Node:
    def __init__(self, is_Word=False):
        self.children = {}
        self.is_Word = is_Word

class TrieDictionary:
    def __init__(self, words=Tuple()):
        self.root = Node()
        for Word in words:
            self.add(Word)

    def add(self, Word):
        node = self.root
        for c in Word:
            node = node.children.setdefault(c, Node())
        node.is_Word = True

    def lookup(self, Word, from_node=None):
        node = self.root if from_node is None else from_node
        for c in Word:
            try:
                node = node.children[c]
            except KeyError:
                return None

        return node

Wir können dann ein Trie -basiertes Wörterbuch aus einer Reihe von Wörtern erstellen:

dictionary = {"a", "pea", "nut", "peanut", "but", "butt", "butte", "butter"}
trie_dictionary = TrieDictionary(words=dictionary)

Was einen Baum hervorbringt, der so aussieht (* kennzeichnet den Anfang oder das Ende eines Wortes):

* -> a*
 \\\ 
  \\\-> p -> e -> a*
   \\              \-> n -> u -> t*
    \\
     \\-> b -> u -> t*
      \\             \-> t*
       \\                 \-> e*
        \\                     \-> r*
         \
          \-> n -> u -> t*

Wir können dies in eine Lösung integrieren, indem wir es mit einer Heuristik über die Auswahl von Wörtern kombinieren. Zum Beispiel können wir längere Wörter kürzeren vorziehen:

def using_trie_longest_Word_heuristic(s):
    node = None
    possible_indexes = []

    # O(1) short-circuit if whole string is a Word, doesn't go against longest-Word wins
    if s in dictionary:
        return [ s ]

    for i in range(len(s)):
        # traverse the trie, char-wise to determine intermediate words
        node = trie_dictionary.lookup(s[i], from_node=node)

        # no more words start this way
        if node is None:
            # iterate words we have encountered from biggest to smallest
            for possible in possible_indexes[::-1]:
                # recurse to attempt to solve the remaining sub-string
                end_of_phrase = using_trie_longest_Word_heuristic(s[possible+1:])

                # if we have a solution, return this Word + our solution
                if end_of_phrase:
                    return [ s[:possible+1] ] + end_of_phrase

            # unsolvable
            break

        # if this is a leaf, append the index to the possible words list
        Elif node.is_Word:
            possible_indexes.append(i)

    # empty string OR unsolvable case 
    return []

Wir können diese Funktion wie folgt verwenden:

>>> using_trie_longest_Word_heuristic("peanutbutter")
[ "peanut", "butter" ]

Da wir unsere Position im Trie beibehalten, während wir nach immer längeren Wörtern suchen, durchlaufen wir trie höchstens einmal pro möglicher Lösung (anstatt 2 mal für peanut: pea, peanut). Der letzte Kurzschluss erspart es uns, im schlimmsten Fall char-weise durch die Saite zu laufen.

Das Endergebnis sind nur wenige Inspektionen:

'peanutbutter' - not a Word, go charwise
'p' - in trie, use this node
'e' - in trie, use this node
'a' - in trie and Edge, store potential Word and use this node
'n' - in trie, use this node
'u' - in trie, use this node
't' - in trie and Edge, store potential Word and use this node
'b' - not in trie from `peanut` vector
'butter' - remainder of longest is a Word

Ein Vorteil dieser Lösung besteht darin, dass Sie sehr schnell wissen, ob längere Wörter mit einem bestimmten Präfix vorhanden sind, sodass Sequenzkombinationen nicht ausführlich mit einem Wörterbuch verglichen werden müssen. Es macht auch das Erhalten einer unsolvable Antwort im Vergleich zu anderen Implementierungen vergleichsweise billig.

Die Nachteile dieser Lösung sind ein großer Speicherbedarf für die trie und die Kosten für den Aufbau der trie im Voraus.

0
Matthew Story

Für die deutsche Sprache gibt es CharSplit, das maschinelles Lernen verwendet und sich gut für Zeichenfolgen mit wenigen Wörtern eignet.

https://github.com/dtuggener/CharSplit

0
Karl Adler

Basierend auf der Lösung von unutbu habe ich eine Java Version implementiert:

private static List<String> splitWordWithoutSpaces(String instring, String suffix) {
    if(isAWord(instring)) {
        if(suffix.length() > 0) {
            List<String> rest = splitWordWithoutSpaces(suffix, "");
            if(rest.size() > 0) {
                List<String> solutions = new LinkedList<>();
                solutions.add(instring);
                solutions.addAll(rest);
                return solutions;
            }
        } else {
            List<String> solutions = new LinkedList<>();
            solutions.add(instring);
            return solutions;
        }

    }
    if(instring.length() > 1) {
        String newString = instring.substring(0, instring.length()-1);
        suffix = instring.charAt(instring.length()-1) + suffix;
        List<String> rest = splitWordWithoutSpaces(newString, suffix);
        return rest;
    }
    return Collections.EMPTY_LIST;
}

Eingabe:"tableapplechairtablecupboard"

Ausgabe:[table, Apple, chair, table, cupboard]

Eingabe:"tableprechaun"

Ausgabe:[tab, leprechaun]

Es scheint, als würde ein ziemlich banales Backtracking ausreichen. Beginnen Sie am Anfang der Zeichenfolge. Scannen Sie nach rechts, bis Sie ein Wort haben. Rufen Sie dann die Funktion für den Rest der Zeichenfolge auf. Die Funktion gibt "false" zurück, wenn sie ganz nach rechts scannt, ohne ein Wort zu erkennen. Andernfalls werden das gefundene Wort und die Liste der vom rekursiven Aufruf zurückgegebenen Wörter zurückgegeben.

Beispiel: "tableapple". Findet "tab", dann "leap", aber kein Wort in "ple". Kein anderes Wort in "leapple". Findet "table" und dann "app". "le" kein Wort, also versucht Apple, erkennt, kehrt zurück.

Um so lange wie möglich zu bleiben, sollten Sie nur die richtigen Lösungen herausgeben (anstatt sie zurückzugeben). Wählen Sie dann nach einem beliebigen Kriterium (maxmax, minmax, average usw.) das optimale aus.

0
Patrick87