Как разбить строку на слова. Пример: "stringintowords" - > "строка в слова"?

Как правильно разбить строку на слова ? (строка не содержит пробелов или знаков препинания)

например: "stringintowords" - > "строка в слова"

не могли бы вы посоветовать, какой алгоритм следует использовать здесь ?

! Update: для тех, кто думает, что этот вопрос просто для любопытства. Этот алгоритм может быть использован для camelcase доменных имен ("sportandfishing .com "- > " SportAndFishing .com"), и этот algo в настоящее время используется aboutus dot org для динамического преобразования.

13 ответов


Как упоминалось многими здесь, это стандартная, простая проблема динамического программирования: лучшее решение дано Фальком Хюффнером. Дополнительная информация хотя:

(a) вы должны рассмотреть возможность реализации isWord С trie, который сэкономит вам много времени, если вы используете правильно (то есть путем постепенного тестирования слов).

(b) ввод "динамическое программирование сегментации" дает оценку более подробных ответов, из лекций университетского уровня с алгоритм псевдокода, такой как эта лекция у Дюка (который даже доходит до того, чтобы обеспечить простой вероятностный подход к тому, что делать, когда у вас есть слова, которые не будут содержаться в любом словаре).


предположим, что у вас есть функция isWord(w), которая проверяет, является ли w - это слово, используя словарь. Давайте для простоты также предположим, что вы только хотите знать, является ли какое-либо слово w такое разделение возможно. Это можно легко сделать с помощью динамического программирования.

пусть S[1..length(w)] быть таблицей с логическими записями. S[i] true если слово w[1..i] можно разделить. Затем установите S[1] = isWord(w[1]) и for i=2 to length(w) вычислить

S[i] = (isWord[w[1..i] или для любого j в {2..i}: S[j-1] и isWord[j..я.)]

это занимает O(length (w)^2) время, если запросы словаря являются постоянным временем. Чтобы действительно найти разделение, просто сохраните выигрышный Сплит в каждом S[i], который установлен в true. Это также может быть адаптировано для перечисления всего решения путем хранения всех таких разбиений.


если вы хотите убедиться, что вы получите это право, вы будете есть использовать подход на основе словаря, и это будет ужасно неэффективно. Вам также придется ожидать получения нескольких результатов от вашего алгоритма.

например: windowsteamblog (of http://windowsteamblog.com/ слава)

  • windows team blog
  • window steam blog

в академической литературе должно быть достаточно много об этом. Ключевые слова для поиска слово сегментация. этой статье выглядит многообещающе, например.

В общем, вы, вероятно, захотите узнать о марковских моделей и алгоритм Витерби. Последний представляет собой алгоритм динамического программирования, который может позволить вам найти правдоподобные сегментации для строки без исчерпывающего тестирования всех возможных сегментация. Существенное понимание здесь заключается в том, что если у вас есть n возможных сегментов для первых M символов, и вы хотите только найти наиболее вероятную сегментацию, вам не нужно оценивать каждый из них по последующим символам - вам нужно только продолжить оценку наиболее вероятного.


считайте количество возможных расщеплений для данной строки. Если у вас есть n символы в строке, есть n-1 возможные места для разделения. Например, для строки cat, вы можете разделить до a и вы можете разделить до t. Это приводит к 4 возможным расщеплениям.

вы можете посмотреть на эту проблему, как выбрать, где вам нужно разделить строку. Вам также нужно выбрать, сколько расколов будет. Так что есть Sum(i = 0 to n - 1, n - 1 choose i) возможно расщепление. К Теорема Биномиального Коэффициента, когда x и y оба равны 1, это равно pow (2, n-1).

конечно, многие из этих вычислений опираются на общие подзадачи, поэтому Динамическое Программирование может ускорить ваш алгоритм. С моей макушки, вычисляя boolean matrix M such M[i,j] is true if and only if the substring of your given string from i to j is a word помогло бы совсем немного. У вас все еще есть экспоненциальное число возможных сегментов, но вы быстро сможете устранить сегментацию, если раннее раскол не складывался в слова. Тогда решением будет последовательность целых чисел (i0, j0, i1, j1,...) при условии, что j sub k = i sub (k + 1).

если ваша цель правильно url верблюда случае, я бы обойти проблему и пойти на что-то немного более прямое: получить домашнюю страницу для URL, удалить любые пробелы и капитализацию из исходного HTML, и поиск строки. Если есть совпадение, найдите этот раздел в исходном HTML и верните его. Вам понадобится массив NumSpaces, который объявляет, сколько пробелов происходит в исходной строке следующим образом:

Needle:       isashort    
Haystack:     This is a short phrase    
Preprocessed: thisisashortphrase   
NumSpaces   : 000011233333444444 

и ваш ответ пришел бы от:

location = prepocessed.Search(Needle)
locationInOriginal = location + NumSpaces[location]
originalLength = Needle.length() + NumSpaces[location + needle.length()] - NumSpaces[location]
Haystack.substring(locationInOriginal, originalLength)

конечно, это сломается, если madduckets.com не было "Mad Duckets" где-то на домашней странице. Увы, это цена, которую вы платите за то, чтобы избежать экспоненциальной проблемы.


Это в основном вариации проблема с рюкзаком, поэтому вам нужен полный список слов и любых решений, описанных в Wiki.

с довольно большим словарем это будет безумно ресурсоемкая и длительная операция, и вы даже не можете быть уверены, что эта проблема будет решена.


Создайте список возможных слов, отсортируйте его от длинных слов до коротких слов.

проверьте, если каждая запись в списке против первой части строки. Если он равен, удалите это и добавьте его в свое предложение с пробелом. Повторить это.


Это действительно можно сделать (в определенной степени) без словаря. По сути, это проблема неконтролируемой сегментации слов. Вам нужно собрать большой список доменных имен, применить алгоритм обучения неконтролируемой сегментации (например,Morfessor) и применить изученную модель для новых доменных имен. Я не уверен, насколько хорошо это будет работать (но это было бы интересно).


на самом деле, со словарем эта проблема может быть решена в O(n) времени. Точнее в (k + 1) * n в худшем случае, где n - количество символов в строке и k - это длина самого длинного слова в словаре.

кроме того, алгоритм позволяет пропускать ненужные.

вот рабочая реализация в Common Lisp, которую я создал некоторое время назад:https://gist.github.com/3381522


единственный способ разбить эту строку на слова-использовать словарь. Хотя это, вероятно, будет довольно ресурсоемким.


лучше всего было бы сравнить подстроку из 0 со словарем, и когда вы нашли совпадение, чтобы извлечь это слово и начать новый поиск словаря с этой точки... но это будет очень подвержено ошибкам, и у вас будут проблемы с множественными и апострофами (раковины, раковины) и другими частями речи.

редактировать

будет "singleemotion" стать "единой эмоции" или "грех" хоре "движения"?


Я смотрел на проблему и думал, может быть, я мог бы поделиться, как я это сделал. Слишком сложно объяснить мой алгоритм словами, поэтому, возможно, я мог бы поделиться своим оптимизированным решением в псевдокоде:

string mainword = "stringintowords";
array substrings = get_all_substrings(mainword);

/** this way, one does not check the dictionary to check for word validity 
 *  on every substring; It would only be queried once and for all, 
 *  eliminating multiple travels to the data storage
 */
string query = "select word from dictionary where word in " + substrings;
array validwords = execute(query).getArray();

validwords = validwords.sort(length, desc);

array segments = [];
while(mainword != ""){
    for(x = 0; x < validwords.length; x++){
        if(mainword.startswith(validwords[x])) {
            segments.push(validwords[x]);
            mainword = mainword.remove(v);
            x = 0;
        }
    }

    /**
     * remove the first character if any of valid words do not match, then start again
     * you may need to add the first character to the result if you want to
     */
    mainword = mainword.substring(1);
}

string result = segments.join(" ");

простое решение Java, которое имеет O (n^2) Время работы.

public class Solution {
    // should contain the list of all words, or you can use any other data structure (e.g. a Trie)
    private HashSet<String> dictionary;

    public String parse(String s) {
        return parse(s, new HashMap<String, String>());
    }

    public String parse(String s, HashMap<String, String> map) {
        if (map.containsKey(s)) {
            return map.get(s);
        }
        if (dictionary.contains(s)) {
            return s;
        }
        for (int left = 1; left < s.length(); left++) {
            String leftSub = s.substring(0, left);
            if (!dictionary.contains(leftSub)) {
                continue;
            }
            String rightSub = s.substring(left);
            String rightParsed = parse(rightSub, map);
            if (rightParsed != null) {
                String parsed = leftSub + " " + rightParsed;
                map.put(s, parsed);
                return parsed;
            }
        }
        map.put(s, null);
        return null;
    }
}