¿Hay una manera eficiente de comparar la similitud de una cadena con cada permutación de otra cadena (es decir, un grupo simétrico)?

Tienes dos problemas aquí. El primero es encontrar una permutación que maximice el número de coincidencias. Esto se puede lograr en tiempo lineal O (n). Comenzaría clasificando ambas cadenas usando un orden de conteo, que tiene una complejidad temporal de N. Luego invertiría las dos permutaciones, que también se pueden lograr en O (N). Entonces aquí puede ejecutar un algoritmo de “coincidencia” en tiempo lineal en la segunda cadena. Empieza asignando todas las A, luego las B y luego las C restando básicamente los recuentos del paso anterior. Luego haces otra carrera para (aleatoriamente o en orden) asignar los caracteres no utilizados. Una vez que haya hecho la coincidencia en la versión ordenada, puede aplicar el inverso de la primera cadena y obtendrá una permutación de la segunda cadena que coincida al máximo con la primera.

Aquí la clave es que esta operación preservará el número de coincidencias, ya que los caracteres que coinciden entre las cadenas ordenadas también coincidirán entre los “No clasificados”. Sin embargo, la única propiedad de las permutaciones que se ha utilizado es el hecho de que las permutaciones tienen inversa, que es una propiedad general de ser un grupo.

Esto es asintóticamente óptimo, ya que para calcular la mejor permutación necesita leer todos los caracteres al menos una vez.

Luego está el problema de encontrar todas (o muchas) de las permutaciones que son igualmente buenas. Aquí no tenemos las propiedades de grupo agradables porque la composición de dos permutaciones que dejan invariante el número de coincidencias puede no dejar invariable el número de coincidencias.

Si uno de los caracteres en la segunda cadena aparece menos que en la primera cadena, esto dejará un “espacio”, un carácter que no puede coincidir. Por ejemplo, si tienes:

ABABAC
FAACXR

Podrás unir las A como:

ABABAC
A -A – x –

ABABAC
A -x – A –

ABABAC
x – A – A –

Tenga en cuenta que puede identificar el número de espacios para cada letra comparando los recuentos después del orden de conteo. Aquí puede calcular fácilmente la cantidad de resultados que obtendrá. Este será el producto de todas las distribuciones posibles de huecos para cada personaje (la combinación del número de lugares en el número de huecos), estos son únicos, y luego todas las combinaciones posibles para llenar esos huecos. Luego, tendrás que multiplicar por la cantidad de formas de distribuir los caracteres restantes. Este será el factorial del número total de caracteres no coincidentes dividido por el producto de los factores de las repeticiones de cada carácter no coincidente (es decir (A + B + C + D)! / A! B! C! D! Donde A, B, C y D son los caracteres de la cadena 2 que no pudo asignar a un carácter de la cadena 1.

En cuanto a imprimirlo, mi única idea es iterar a través de todas las permutaciones. Esto le dará una complejidad temporal de N, que es asintóticamente óptima si planea imprimir / almacenar la permutación completa, necesitará una operación para cada personaje.

Esto no usa demasiadas propiedades de permutaciones, pero dado que tiene una complejidad lineal de tiempo, sería realmente difícil mejorarlo.

EDITAR: ejemplo de código agregado para el problema 1, ya que la explicación puede ser difícil de seguir.

#include

// Proporciona una permutación de originalString que está más cerca de targetString en O (n)
unsigned char *
findPermutation (const unsigned char * targetString, const unsigned char * originalString) {
unsigned int length = strlen ((const char *) originalString);
if (strlen ((const char *) targetString)! = length) devuelve NULL;
int countOriginal [256], scratchSpace [256];
int * countTarget = new int [257];
int * inversePermutation = new int [longitud];
unsigned char * opticalMatch = new unsigned char [longitud];
countTarget [0] = 0; countTarget–;

// Inicializar recuentos
para (int i = 0; i <256; i ++) {
countOriginal [i] = 0;
countTarget [i] = 0;
scratchSpace [i] = 0;
}
// Ordenar contando
for (unsigned int i = 0; i <length; i ++) {
countOriginal [originalString [i]] ++;
countTarget [targetString [i]] ++;
}

// Acumulación de recuento
para (int i = 1; i <256; i ++) countTarget [i] + = countTarget [i-1];

// Invertir permutación
for (unsigned int i = 0; i <length; i ++) {
inversePermutation [countTarget [targetString [i] -1] + scratchSpace [targetString [i]]] = i;
scratchSpace [targetString [i]] ++;
}
para (int i = 0; i <256; i ++) scratchSpace [i] = 0;

// Partido
para (int i = 0; i <256; i ++) {
para (int j = 0; ((j <countOriginal [i]) && (j <(countTarget [i] -countTarget [i-1]))); j ++) {
óptimaMatch [inversePermutation [(scratchSpace [i] ++) + countTarget [i-1]]] = i;
}
}
// Asigna caracteres no coincidentes
int targetChar = 0;
para (int i = 0; i <256; i ++) countOriginal [i] – = scratchSpace [i];
para (int i = 0; i <256; i ++) {
for (int j = 0; j <countOriginal [i]; j ++) {
while ((countTarget [targetChar] -countTarget [targetChar-1] -scratchSpace [targetChar]) == 0) targetChar ++;
óptimaMatch [inversePermutation [countTarget [targetChar-1] + scratchSpace [targetChar] ++]] = i;
}
}

// Limpiar
countTarget ++;
eliminar [] inversePermutation;
eliminar [] countTarget;
volver óptimoMatch;
}

En t
main (int argc, char * argv []) {
if (argc! = 3) {
std :: cout << "Debe usar dos cadenas como parámetros. \ n";
salida (1);
}
unsigned char * resultString = findPermutation ((const unsigned char *) argv [1], (const unsigned char *) argv [2]);
if (resultString == NULL) {
std :: cout << "Las dos cadenas deben ser de igual longitud \ n";
salida (1);
}
std :: cout << ((char *) resultString) << '\ n';
eliminar [] resultanteString;
}

Marcs-MacBook-Pro: ~ marc $ ./a.out alegres ratones
eosmus

No estoy seguro de entender la pregunta, así que avíseme si esto realmente lo aborda.

Si solo le interesa cuán cerca se acerca una cadena a las permutaciones de otra, ¿no tiene que ignorar el orden de los caracteres en la cadena? ¿No es suficiente contar el número de ocurrencias de caracteres en las cadenas y comparar los recuentos?

Por ejemplo, tome TOTAL y BOLTS. Cuente sus elementos, simplemente tomando ABLOST como el alfabeto, y obtendrá 101102 y 011111. Cambiar una A por una B y T por una S les da los mismos recuentos. Por lo tanto, difieren en dos caracteres y alguna permutación.

More Interesting

¿Cuál es la forma más sencilla de entender las máquinas de Turing y el problema del castor ocupado?

¿Habría algún límite matemático potencial para una máquina física con el propósito de replicarse a sí mismo?

¿Todas las integrales pueden ser calculadas por una computadora? Del mismo modo, ¿hay integrales en este momento que los matemáticos no puedan resolver?

Cómo ser bueno en matemáticas para la programación competitiva

¿Cuáles son algunas aplicaciones del mundo real de min-cut en la teoría de grafos?

Como una niña india de 23 años, he completado mi licenciatura en tecnología. Me interesa la fotografía y la quiero como mi profesión. ¿Hay alguna forma de convencer a mi papá? ¿Qué tengo que hacer?

Si Kurt Godel estuviera vivo hoy, ¿podría probar el problema P vs NP?

¿Qué es una prueba intuitiva de que las redes neuronales recurrentes pueden calcular cualquier función computable por una máquina Turing?

¿Cómo se puede encontrar el número de iteraciones requeridas para la integración usando la regla de Simpson para una precisión dada?

Para alguien que no sabe nada de informática, ¿por qué el conocimiento de las matemáticas es tan importante para ser un buen programador?

Dada una matriz, ¿qué es un algoritmo para identificar la submatriz con la suma máxima?

¿Cuál es la mejor manera de aprender geometría algebraica si uno no está interesado en usarlo para propósitos teóricos numéricos, sino más bien para aplicaciones en física teórica e informática teórica?

A los 27 años, ¿soy demasiado viejo para aprender matemáticas avanzadas?

¿En qué se diferencia la informática de las matemáticas para resolver un problema?

¿Cuál es una explicación para esta ecuación de números de punto fijo con signo?