Найти несколько вхождений слов в строке и сохранить соответствующие начальные индексы - PullRequest
0 голосов
/ 06 июня 2019

ФОН

У меня есть строка текста и хэш-набор, содержащий слова, которые я ищу.

Учитывая

String doc = "one of the car and bike and one of those";
String [] testDoc = doc.split("\\s+");
HashSet<String> setW = new HashSet<>();
setW.add("and");
setW.add("of");
setW.add("one");

ЗАДАЧА

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

В вышеприведенном случае мы можем сохранить следующее

one-->0 

of-->4 

and-->15 

and-->24, 

one-->28, 

of-->32

` ПОПЫТКА

//create hashmap
for(int i = 0; i<testDoc.length; i++){
    if(setW.contains(testDoc[i])) {
        doc.indexOf(testDoc[i]);
       //add string and its index to hashmap
    }

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

Я бы хотел получить здесь какой-то вклад.

Ответы [ 3 ]

3 голосов
/ 06 июня 2019

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

Обратите внимание, что вы можете удалить свой тест для contains(), чтобы не искать строку дважды.

0 голосов
/ 07 июня 2019

Ну, есть другое решение, если вы хотите сделать меньше итераций, этот код перебирает строку один раз. Я думал о доступе к строке символ за символом. Я взял один StringBuilder, чтобы добавить каждый символ и проверить, когда вы получите пробел, просто добавьте эту строку в окончательный список ответов, а также добавьте индекс. Я описал свой подход, как показано ниже, и я думаю, что он просто посещает каждый символ один раз, временная сложность для этого кода O (n).

StringBuilder sb=new StringBuilder();
    ArrayList<String> answer=new ArrayList<>();
    ArrayList<Integer> index=new ArrayList<>();
    HashSet<String> setW = new HashSet<>();
    setW.add("and");
    setW.add("of");
    setW.add("one");
    index.add(0);
    String doc = "one of the car and bike and one of those";
    for(int i=0;i<doc.length();i++){
        if(i==doc.length() || doc.charAt(i)==' '){
            index.add(i+1);
            answer.add(sb.toString());
            sb=new StringBuilder();
            i++;
        }
        sb.append(doc.charAt(i));
        if(i==doc.length()-1){
            if(setW.contains(sb.toString())){
                answer.add(sb.toString());
            };
        }
    }
    for(int i=0;i<answer.size();i++){
        if(setW.contains(answer.get(i))){
            System.out.println(answer.get(i)+"-->"+index.get(i));
        }
    }

Я получил ожидаемый результат, основанный на этой идее, и причина отправки моего ответа на этот вопрос заключается в том, чтобы найти другое возможное решение. (В ответе на HashSet мы получим индекс каждого слова, не только существующего в setW, поэтому, если вы не хотите, вы можете удалить его, используя один if (! SetW.contains (answer.get (i)) состояние.)

выход

one-->0
of-->4
and-->15
and-->24
one-->28
of-->32
0 голосов
/ 06 июня 2019

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

Например, ваши 3 слова будут регулярным выражением, подобным следующему:

and|of|one

Конечно, вам не нужны частичные слова, поэтому вы бы добавили проверку границы слова:

\b(and|of|one)\b

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

Хотя с чистыми словами (всеми буквами) никогда не будет проблем, неплохо было бы охранять регулярное выражение, заключая слова в кавычки, используя Pattern.quote().

Пример

String doc = "one of the car and bike and one of those";
String[] words = { "and", "of", "one" };

// Build regex
StringJoiner joiner = new StringJoiner("|", "\\b(?:", ")\\b");
for (String word : words)
    joiner.add(Pattern.quote(word));
String regex = joiner.toString();

// Find words
for (Matcher m = Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(doc); m.find(); )
    System.out.println(m.group() + "-->" + m.start());

Выход

one-->0
of-->4
and-->15
and-->24
one-->28
of-->32

Если вы хотите сжать (скрыть)немного кодировать, вы можете написать это как один оператор в Java 9 +:

Pattern.compile(Stream.of(words).collect(joining("|", "(?i)\\b(?:", ")\\b"))).matcher(doc).results().forEach(r -> System.out.println(r.group() + "-->" + r.start()));

Вывод такой же.

...