Questa risposta è attualmente interrotta a causa di un bug. Correzione presto ...
C, 2 stringhe in ~ 35 secondi
Questo è davvero un lavoro in corso (come dimostrato dall'orribile disordine), ma speriamo che dia delle buone risposte!
Il codice:
#include "stdlib.h"
#include "string.h"
#include "stdio.h"
#include "time.h"
/* For the versatility */
#define MIN_CODEPOINT 0x30
#define MAX_CODEPOINT 0x3F
#define NUM_CODEPOINT (MAX_CODEPOINT - MIN_CODEPOINT + 1)
#define CTOI(c) (c - MIN_CODEPOINT)
#define SIZE_ARRAY(x) (sizeof(x) / sizeof(*x))
int LCS(char** str, int num);
int getshared(char** str, int num);
int strcount(char* str, char c);
int main(int argc, char** argv) {
char** str = NULL;
int num = 0;
int need_free = 0;
if (argc > 1) {
str = &argv[1];
num = argc - 1;
}
else {
scanf(" %d", &num);
str = malloc(sizeof(char*) * num);
if (!str) {
printf("Allocation error!\n");
return 1;
}
int i;
for (i = 0; i < num; i++) {
/* No string will exceed 1000 characters */
str[i] = malloc(1001);
if (!str[i]) {
printf("Allocation error!\n");
return 1;
}
scanf(" %1000s", str[i]);
str[i][1000] = '\0';
}
need_free = 1;
}
clock_t start = clock();
/* The call to LCS */
int size = LCS(str, num);
double dt = ((double)(clock() - start)) / CLOCKS_PER_SEC;
printf("Size: %d\n", size);
printf("Elapsed time on LCS call: %lf s\n", dt);
if (need_free) {
int i;
for (i = 0; i < num; i++) {
free(str[i]);
}
free(str);
}
return 0;
}
/* Some terribly ugly global variables... */
int depth, maximum, mapsize, lenmap[999999][2];
char* (strmap[999999][20]);
char outputstr[1000];
/* Solves the LCS problem on num strings str of lengths len */
int LCS(char** str, int num) {
/* Counting variables */
int i, j;
if (depth + getshared(str, num) <= maximum) {
return 0;
}
char* replace[num];
char match;
char best_match = 0;
int earliest_set = 0;
int earliest[num];
int all_late;
int all_early;
int future;
int best_future = 0;
int need_future = 1;
for (j = 0; j < mapsize; j++) {
for (i = 0; i < num; i++)
if (str[i] != strmap[j][i])
break;
if (i == num) {
best_match = lenmap[j][0];
best_future = lenmap[j][1];
need_future = 0;
if (best_future + depth < maximum || !best_match)
goto J;
else {
match = best_match;
goto K;
}
}
}
for (match = MIN_CODEPOINT; need_future && match <= MAX_CODEPOINT; match++) {
K:
future = 1;
all_late = earliest_set;
all_early = 1;
for (i = 0; i < num; replace[i++]++) {
replace[i] = strchr(str[i], match);
if (!replace[i]) {
future = 0;
break;
}
if (all_early && earliest_set && replace[i] - str[i] > earliest[i])
all_early = 0;
if (all_late && replace[i] - str[i] < earliest[i])
all_late = 0;
}
if (all_late) {
future = 0;
}
I:
if (future) {
if (all_early || !earliest_set) {
for (i = 0; i < num; i++) {
earliest[i] = (int)(replace[i] - str[i]);
}
}
/* The recursive bit */
depth++;
future += LCS(replace, num);
depth--;
best_future = future > best_future ? (best_match = match), future : best_future;
}
}
if (need_future) {
for (i = 0; i < num; i++)
strmap[mapsize][i] = str[i];
lenmap[mapsize][0] = best_match;
lenmap[mapsize++][1] = best_future;
}
J:
if (depth + best_future >= maximum) {
maximum = depth + best_future;
outputstr[depth] = best_match;
}
if (!depth) {
mapsize = 0;
maximum = 0;
puts(outputstr);
}
return best_future;
}
/* Return the number of characters total (not necessarily in order) that the strings all share */
int getshared(char** str, int num) {
int c, i, tot = 0, min;
for (c = MIN_CODEPOINT; c <= MAX_CODEPOINT; c++) {
min = strcount(str[0], c);
for (i = 1; i < num; i++) {
int count = strcount(str[i], c);
if (count < min) {
min = count;
}
}
tot += min;
}
return tot;
}
/* Count the number of occurrences of c in str */
int strcount(char* str, char c) {
int tot = 0;
while ((str = strchr(str, c))) {
str++, tot++;
}
return tot;
}
La funzione rilevante che esegue tutto il calcolo LCS è la funzione LCS
. Il codice sopra indicherà la propria chiamata a questa funzione.
Salva come main.c
e compila con:gcc -Ofast main.c -o FLCS
Il codice può essere eseguito con argomenti da riga di comando o tramite stdin. Quando si utilizza lo stdin, si aspetta un numero di stringhe seguite dalle stringhe stesse.
~ Me$ ./FLCS "12345" "23456"
2345
Size: 4
Elapsed time on LCS call: 0.000056 s
O:
~ Me$ ./FLCS
6
2341582491248123139182371298371239813
2348273123412983476192387461293472793
1234123948719873491234120348723412349
1234129388234888234812834881423412373
1111111112341234128469128377293477377
1234691237419274737912387476371777273
1241231212323
Size: 13
Elapsed time on LCS call: 0.001594 s
Su un Mac OS X con un Intel Core i7 da 1,7 Ghz e il test case fornito da Dennis, otteniamo il seguente output per 2 stringhe:
16638018800200>3??32322701784=4240;24331395?<;=99=?;178675;866002==23?87?>978891>=9<66=381992>>7030829?25265804:=3>:;60<9384=081;08?66=51?0;509072488>2>924>>>3175?::<9199;330:494<51:>748571?153994<45<??20>=3991=<962508?7<2382?489
Size: 386
Elapsed time on LCS call: 33.245087 s
L'approccio è molto simile al mio approccio alla sfida precedente, qui . Oltre all'ottimizzazione precedente, ora controlliamo un numero totale di caratteri condivisi tra le stringhe ad ogni ricorsione e usciamo presto se non c'è modo di ottenere una sottostringa più lunga di quella già esistente.
Per ora, gestisce bene 2 stringhe ma tende a bloccarsi di più. Altri miglioramenti e una spiegazione migliore a venire!