¿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 diferencia entre notación matemática y notación de programación? ¿Por qué usar uno sobre el otro? ¿Por qué no solo usar siempre la programación?

¿Cuál es la forma más rápida de determinar si una serpiente tiene un camino hacia su cola?

Cómo resolver la relación de recurrencia T (n + 1) = T (n) + ceil (n + 1) usando la sustitución para hacer un análisis asintótico

¿Cómo sabe la CPU que estamos usando el complemento de uno o el complemento de dos para representar números negativos?

Cómo resolver torres de Hanoi con restricciones adicionales

¿Cómo probarías que el problema máximo de conjunto independiente en los gráficos está en la clase NP?

¿Cuál es el significado de las implicaciones teóricas?

¿Es justo decir que las matemáticas, la informática y la programación se encuentran en la intersección de todas las materias?

¿Podré enseñarme el currículo de la Academia Phillips Exeter?

¿Puede una máquina de turing aceptar una entrada sin detenerse?

Si tengo una prueba potencial de que P = NP, ¿con quién puedo compartirla para que no me juzguen?

Si resolvemos el problema del ciclo de Hamilton en el tiempo P, ¿eso realmente muestra P = NP?

¿Cuál es la diferencia real entre las aperturas f / 1.8 yf / 2.2 en las lentes de la cámara?

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

Cómo calcular todos los quíntuples ordenados de números primos (a, b, c, d, e) de modo que [matemática] a + \ sqrt {b ^ 2 + c} = \ sqrt {d ^ 2 + e} [/ matemática]