codingbat wordEnds usando regex
Estou tentando resolverwordEnds
de codingbat.com usando regex.
Dada uma sequência de caracteres e uma sequência de palavras não vazia, retorne uma sequência feita de cada caractere imediatamente antes e logo após cada aparição da palavra na sequência. Ignore os casos em que não há caractere antes ou depois da palavra e um caractere pode ser incluído duas vezes se estiver entre duas palavras.
wordEnds("abcXY123XYijk", "XY") → "c13i"
wordEnds("XY123XY", "XY") → "13"
wordEnds("XY1XY", "XY") → "11"
wordEnds("XYXY", "XY") → "XY"
É o mais simples que posso fazer com meu conhecimento atual de regex:
public String wordEnds(String str, String word) {
return str.replaceAll(
".*?(?=word)(?<=(.|^))word(?=(.|$))|.+"
.replace("word", java.util.regex.Pattern.quote(word)),
" de codingbat.com2"
);
}
replace
é usado para colocar no realword
string no padrão para facilitar a leitura.Pattern.quote
não é necessário passar nos testes, mas acho que é necessário para uma solução adequada baseada em regex.
O regex tem duas partes principais:
Se depois de corresponder o mínimo de caracteres possível ".*?
",word
ainda pode ser encontrado "(?=word)
", em seguida, olhe para trás para capturar qualquer caractere imediatamente anterior a ele"(?<=(.|^))
", Combine "word
"e antecipadamente para capturar qualquer caractere a seguir"(?=(.|$))
"O teste inicial "if" garante que a aparência atômica captura apenas se houver umword
O uso de lookahead para capturar o seguinte caractere não o consome, portanto pode ser usado como parte de outras correspondênciasCaso contrário, corresponda ao que resta "|.+
"Os grupos 1 e 2 capturariam cadeias vaziasEu acho que isso funciona em todos os casos, mas é obviamente bastante complexo. Só estou me perguntando se outros podem sugerir um regex mais simples para fazer isso.
Nota: não estou procurando uma solução usandoindexOf
e um loop. Eu quero um baseado em regexreplaceAll
solução. Também preciso de um regex de trabalho que passe em todos os testes de codingbat.
Eu consegui reduzir a ocorrência deword
dentro do padrão para apenas um.
".+?(?<=(^|.)word)(?=(.?))|.+"
Ainda estou procurando se é possível simplificar ainda mais isso, mas também tenho outra pergunta:
Com esse último padrão, simplifiquei.|$
para somente.?
com sucesso, mas se eu também tentasse simplificar^|.
para.?
isso não funciona. Por que é que?