Eu tenho que criar uma função que aceita uma string e ela deve retornar true
ou com false
base em se a entrada consiste em uma sequência de caracteres repetida. O comprimento da sequência especificada é sempre maior que 1
e a sequência de caracteres deve ter pelo menos uma repetição.
"aa" // true(entirely contains two strings "a")
"aaa" //true(entirely contains three string "a")
"abcabcabc" //true(entirely containas three strings "abc")
"aba" //false(At least there should be two same substrings and nothing more)
"ababa" //false("ab" exists twice but "a" is extra so false)
Eu criei a função abaixo:
function check(str){
if(!(str.length && str.length - 1)) return false;
let temp = '';
for(let i = 0;i<=str.length/2;i++){
temp += str[i]
//console.log(str.replace(new RegExp(temp,"g"),''))
if(!str.replace(new RegExp(temp,"g"),'')) return true;
}
return false;
}
console.log(check('aa')) //true
console.log(check('aaa')) //true
console.log(check('abcabcabc')) //true
console.log(check('aba')) //false
console.log(check('ababa')) //false
A verificação disso faz parte do problema real. Não posso pagar uma solução não eficiente como essa. Primeiro de tudo, ele passa pela metade da corda.
O segundo problema é que ele está usando replace()
em cada loop, o que o torna lento. Existe uma solução melhor em relação ao desempenho?
javascript
string
algorithm
Maheer Ali
fonte
fonte
Respostas:
Existe um teorema bacana sobre cordas como essas.
Aqui, uma rotação significa excluir um número de caracteres da frente da string e movê-los para trás. Por exemplo, a string
hello
pode ser girada para formar qualquer uma dessas strings:Para ver por que isso funciona, primeiro, suponha que uma string consista em k cópias repetidas de uma string w. Em seguida, excluir a primeira cópia do padrão repetido (w) da frente da corda e prendê-la na parte de trás dará a mesma sequência. A direção reversa é um pouco mais difícil de provar, mas a idéia é que, se você girar uma sequência e voltar com o que começou, poderá aplicar essa rotação repetidamente para agrupar a sequência com várias cópias do mesmo padrão (esse padrão é o você precisa ir até o final para fazer a rotação).
Agora, a questão é como verificar se esse é o caso. Para isso, há outro belo teorema que podemos usar:
Como exemplo, podemos ver que
lohel
é uma rotação dahello
seguinte maneira:No nosso caso, sabemos que toda string x sempre será uma subcadeia de xx (aparecerá duas vezes, uma vez em cada cópia de x). Então, basicamente, precisamos apenas verificar se nossa string x é uma subcadeia de xx sem permitir que ela corresponda no primeiro caractere ou na metade do caminho. Aqui está uma linha para isso:
Supondo que
indexOf
seja implementado usando um algoritmo de correspondência rápida de strings, ele será executado no tempo O (n), em que n é o comprimento da string de entrada.Espero que isto ajude!
fonte
Você pode fazê-lo por um grupo de captura e backreference . Basta verificar se é a repetição do primeiro valor capturado.
No RegExp acima:
^
e$
significa âncoras de início e fim para prever a posição.(.+)
captura qualquer padrão e captura o valor (exceto\n
).\1
é uma referência anterior ao primeiro valor capturado e\1+
verificaria a repetição do valor capturado.Explicação Regex aqui
Para a depuração do RegExp, use: https://regex101.com/r/pqlAuP/1/debugger
Desempenho: https://jsperf.com/reegx-and-loop/13
fonte
If you use normal (TCS:no backreference, concatenation,alternation,Kleene star) regexp and regexp is already compiled then it's O(n).
mas, ao escrever, você está usando a referência anterior, ainda é O (n)?[\s\S]
vez de,.
se precisar corresponder caracteres de nova linha da mesma maneira que outros caracteres. O caractere de ponto não corresponde às novas linhas; a alternativa procura todos os caracteres de espaço em branco e não em branco, o que significa que as novas linhas são incluídas na correspondência. (Observe que isso é mais rápido que o mais intuitivo(.|[\r\n])
.) No entanto, se a sequência definitivamente não contiver novas linhas, o simples.
será mais rápido. Observe que isso será muito mais simples se o sinalizador dotall for implementado./^(.+?)\1+$/
um pouco mais rápido? (12 passos vs 20 passos)Talvez a abordagem algorítmica mais rápida seja a construção de uma função Z em tempo linear:
Implementação C ++ para referência:
Implementação de JavaScript
Otimizações adicionadas - criação de metade do z-array e saída antecipada
Então você precisa verificar índices
i
que dividem n. Se você achari
que issoi+z[i]=n
, a sequências
poderá ser compactada no comprimentoi
e você poderá retornartrue
.Por exemplo, para
matriz z é
e podemos encontrar isso para
portanto,
s
pode ser representado como substring de comprimento 4 repetido três vezes.fonte
return z.some((zi, i) => (i + zi) === n && n % i === 0)
const check = (s) => { let n = s.length; let z = Array(n).fill(0); for (let i = 1, l = 0, r = 0; i < n; ++i) { if (i <= r) z[i] = Math.min(r - i + 1, z[i - l]); while (i + z[i] < n && s[z[i]] == s[i + z[i]]) ++z[i]; // check condition here and return if (z[i] + i === n && n % i === 0) return true; if (i + z[i] - 1 > r) l = i, r = i + z[i] - 1; } // or return false return false; }
Eu li a resposta de gnasher729 e a implementei. A idéia é que, se houver repetições, deve haver (também) um número primo de repetições.
Um algoritmo ligeiramente diferente é o seguinte:
Atualizei a página jsPerf que contém os algoritmos usados nesta página.
fonte
function*
pela primeira vez como eu, é para declarar um gerador, não uma função regular. Veja MDNSuponha que a sequência S tenha comprimento N e seja feita de duplicatas da substring s, então o comprimento de s divide N. Por exemplo, se S tiver comprimento 15, a substring terá comprimento 1, 3 ou 5.
Seja S feito de (p * q) cópias de s. Então S também é feito de p cópias de (s, repetidas q vezes). Temos, portanto, dois casos: se N é primo ou 1, então S pode ser feito apenas de cópias da substring de comprimento 1. Se N é composto, precisamos apenas verificar substrings s de comprimento N / p para primos p divididos o comprimento de S.
Portanto, determine N = o comprimento de S e encontre todos os fatores primos no tempo O (sqrt (N)). Se houver apenas um fator N, verifique se S é a mesma sequência repetida N vezes, caso contrário, para cada fator primo p, verifique se S consiste em repetições de p dos primeiros caracteres N / p.
fonte
Eu acho que uma função recursiva pode ser muito rápida também. A primeira observação é que o comprimento máximo repetido do padrão é metade do comprimento da string total. E poderíamos testar todos os possíveis comprimentos repetidos de padrões: 1, 2, 3, ..., str.length / 2
A função recursiva isRepeating (p, str) testa se esse padrão é repetido em str.
Se str for maior que o padrão, a recursão exige que a primeira parte (mesmo comprimento que p) seja uma repetição, bem como o restante de str. Então, str é efetivamente dividido em pedaços de comprimento p.length.
Se o padrão e str testados tiverem o mesmo tamanho, a recursão termina aqui com êxito.
Se o comprimento for diferente (acontece para "aba" e padrão "ab") ou se as peças forem diferentes, falso será retornado, propagando a recursão.
Desempenho: https://jsperf.com/reegx-and-loop/13
fonte
if( str===p.repeat(str.length/i) ) return true;
vez de usar uma função recursiva?Escreveu isso em Python. Eu sei que não é a plataforma, mas demorou 30 minutos. PS => PYTHON
fonte
Minha abordagem é semelhante ao gnasher729, na medida em que usa o comprimento potencial da substring como foco principal, mas é menos matemático e intensivo em processos:
L: Comprimento da corda original
S: Comprimentos potenciais de sub-strings válidas
Loop S de (parte inteira de) L / 2 a 1. Se L / S for um número inteiro, verifique sua string original com os primeiros caracteres S da string original repetidos vezes L / S.
O motivo do loop de L / 2 para trás e não de 1 em diante é obter a maior substring possível. Se você deseja o menor loop de substring possível de 1 a L / 2. Exemplo: "abababab" possui "ab" e "abab" como possíveis substrings. Qual dos dois seria mais rápido se você se preocupasse apenas com um resultado verdadeiro / falso, dependendo do tipo de string / substring ao qual será aplicado.
fonte
O código do Mathematica a seguir quase detecta se a lista é repetida pelo menos uma vez. Se a sequência for repetida pelo menos uma vez, ela retornará verdadeiro, mas também poderá retornar verdadeiro se a sequência for uma combinação linear de sequências repetidas.
Esse código procura a contribuição "de comprimento total", que deve ser zero em uma sequência repetida, mas a sequência
accbbd
também é considerada repetida, pois é uma soma das duas sequências repetidasababab
e012012
.A idéia é usar a transformação rápida de Fourier e procurar os espectros de frequência. Olhando para outras frequências, deve-se também detectar esse cenário estranho.
fonte
A idéia básica aqui é examinar qualquer possível substring, começando no comprimento 1 e parando na metade do comprimento da string original. Nós olhamos apenas os comprimentos de substring que dividem o comprimento da string original igualmente (ou seja, str.length% substring.length == 0).
Essa implementação examina o primeiro caractere de cada possível iteração de substring antes de passar para o segundo caractere, o que pode economizar tempo se for esperado que as substrings sejam longas. Se nenhuma incompatibilidade for encontrada após examinar toda a substring, retornaremos true.
Retornamos false quando ficamos sem substrings em potencial para verificar.
fonte
Eu não estou familiarizado com JavaScript, então não sei o quão rápido isso será, mas aqui está uma solução de tempo linear (assumindo uma implementação interna razoável) usando apenas os internos. Vou descrever o algoritmo em pseudocódigo.
A ideia é semelhante à resposta do MBo. Para cada um
i
que divide o comprimento,str
é uma repetição de seus primeirosi
caracteres se e somente se permanecer o mesmo após a troca dei
caracteres.Me vem à mente que esse componente interno pode estar indisponível ou ineficiente. Nesse caso, sempre é possível implementar o algoritmo KMP manualmente, o que requer aproximadamente a mesma quantidade de código que o algoritmo na resposta do MBo.
fonte
i
,s[0:n-i] == s[i:n]
ou equivalentemente,s == s[i:n] + s[0:i]
. Por que a segunda linha precisa determinar se houve alguma repetição?str
ao formuláriot
e depois digitalizat
para tentar encontrar ostr
interiort
. Ok, isso pode funcionar (retirei meu voto negativo). Não é linear em strlen (str), no entanto. Dizer que ostr
comprimento é L. Então, em cada posição p = 0,1,2, ..., verificando se str [0..L-1] == t [p..p + L-1] recebe O (L ) Tempo. Você precisa fazer verificações O (L) à medida que passa pelos valores de p, então é O (L ^ 2).Uma das idéias simples é substituir a string pela substring de "" e, se houver algum texto, ele será falso, caso contrário, será verdade.
fonte