C 618 564 bayt
d,M,N,A[9999][2];char*(R[9999][20]),b[1000];L(char**s,n){char*j[20],c,a=0;int x[n],y=n-1,z,i,t,m=0,w=1;for(;y;)x[y--]=999;for(;y<N;y++){for(i=0;i<n&&s[i]==R[y][i];i++);if(i/n){a=A[y][0];m=A[y][1];w=0;if(m+d<M||!a)goto J;else{c=a;goto K;}}}for(c=97;w&&c<'{';c++){K:t=1,y=1,z=1;for(i=0;i<n;j[i++]++){for(j[i]=s[i];*j[i]-c;j[i]++)t&=!!*j[i];y&=j[i]-s[i]>x[i]?z=0,1:0;}t&=!y;I:if(t){if(z)for(i=0;i<n;i++)x[i]=j[i]-s[i];d++,t+=L(j,n),d--,m=t>m?a=c,t:m;}}if(w){for(y=0;y<n;y++)R[N][y]=s[y];A[N][0]=a;A[N++][1]=m;}J:if(d+m>=M)M=d+m,b[d]=a;if(!d)N=0,M=0,puts(b);return m;}
Ve burada "okunabilirlik" için çözülmüş değil:
d,M,N,A[9999][2];
char*(R[9999][20]),b[1000];
L(char**s,n){
char*j[20],c,a=0;
int x[n],y=n-1,z,i,t,m=0,w=1;
for(;y;)
x[y--]=999;
for(;y<N;y++){
for(i=0;i<n&&s[i]==R[y][i];i++);
if(i/n){
a=A[y][0];
m=A[y][1];
w=0;
if(m+d<M||!a)
goto J;
else{
c=a;
goto K;
}
}
}
for(c=97;w&&c<'{';c++){
K:
t=1,
y=1,
z=1;
for(i=0;i<n;j[i++]++){
for(j[i]=s[i];*j[i]-c;j[i]++)
t&=!!*j[i];
y&=j[i]-s[i]>x[i]?z=0,1:0;
}
t&=!y;
I:
if(t){
if(z)
for(i=0;i<n;i++)
x[i]=j[i]-s[i];
d++,
t+=L(j,n),
d--,
m=t>m?a=c,t:m;
}
}
if(w){
for(y=0;y<n;y++)R[N][y]=s[y];
A[N][0]=a;
A[N++][1]=m;
}
J:
if(d+m>=M)
M=d+m,b[d]=a;
if(!d)
N=0,M=0,puts(b);
return m;
}
Bayanlar ve baylar, korkunç bir hata yaptım. Bu kullanılan güzel olmak ... Ve git-az ... En azından artık öyle hızlı .
L
Girdi olarak bir dizi s
karakter dizisi ve n
dize sayısı alan özyinelemeli bir işlev tanımlarız . İşlev, sonuçta elde edilen dizgiyi stdout'a verir ve bu dizenin karakterlerindeki boyutu arada bir döndürür.
Yaklaşım
Kod kıvrımlı olmasına rağmen, buradaki strateji çok karmaşık değil. Sahte kod ile açıklayacağım oldukça saf bir özyinelemeli algoritma ile başlıyoruz:
Function L (array of strings s, number of strings n), returns length:
Create array of strings j of size n;
For each character c in "a-z",
For each integer i less than n,
Set the i'th string of j to the i'th string of s, starting at the first appearance of c in s[i]. (e.g. j[i][0] == c)
If c does not occur in the i'th string of s, continue on to the next c.
end For
new_length := L( j, n ) + 1; // (C) t = new_length
if new_length > best_length
best_character := c; // (C) a = best_character
best_length := new_length; // (C) m = best_length
end if
end For
// (C) d = current_depth_in_recursion_tree
if best_length + current_depth_in_recursion_tree >= best_found
prepend best_character to output_string // (C) b = output_string
// (C) M = best_found, which represents the longest common substring found at any given point in the execution.
best_found = best_length + current_depth;
end if
if current_depth_in_recursion_tree == 0
reset all variables, print output_string
end if
return best_length
Şimdi, bu algoritma kendi başına oldukça iğrenç (ama yaklaşık ~ 230 bayt sığabilir, buldum). Bu şekilde hızlı sonuçlar elde edilemez. Bu algoritma, dize uzunluğu ile inanılmaz derecede kötü ölçekleniyor. Bununla birlikte, bu algoritma çok sayıda dizeyle oldukça iyi ölçeklenir. Son test senaryosu neredeyse anında çözülebilir, çünkü hiçbir dizgenin ortak s
karakteri yoktur c
. Yukarıda uyguladığım, inanılmaz bir hız artışı ile sonuçlanan iki ana hile vardı:
Her çağrıda L
, daha önce aynı girişin bize verilip verilmediğini kontrol edin. Pratikte bilgiler işaretçiler aracılığıyla aynı dizelere aktarıldığından, aslında dizeleri karşılaştırmak zorunda değiliz , sadece konumlar, bu harika. Bu bilgiyi daha önce aldığımızı fark edersek, hesaplamalardan geçmeye gerek yoktur (çoğu zaman, ancak çıktı almak bunu biraz daha karmaşık hale getirir) ve sadece uzunluğu döndürmekten kurtulabiliriz. Bunu yaparsak değil bir eşleşme bulmak, gelecek çağrılara karşılaştırmak için giriş / çıkış bu seti kaydedin. C kodunda, ikinci for
döngü girişle eşleşmeyi bulmaya çalışır. Bilinen giriş işaretçileri kaydedilir R
ve karşılık gelen uzunluk ve karakter çıkış değerleriA
. Bu planın çalışma süresi üzerinde, özellikle daha uzun dizelerle büyük bir etkisi oldu.
Her zaman biz yerlerini bulmak c
içinde s
, ne bulduk optimum olmadığını biz derhal kapalı bildiğim bir şans var. Her konumda olursa c
göründüğünü sonra başka mektupta bazı bilinen yer, biz otomatik olarak biliyorum bu o c
Eğer içinde bir daha mektup sığabilecek çünkü optimal alt dize yol açmaz. Bu, küçük bir maliyetle, L
büyük dizeler için yüzlerce çağrıyı potansiyel olarak kaldırabileceğimiz anlamına gelir . Yukarıdaki C kodunda, y
otomatik olarak bu karakterin yetersiz bir dizeye yol açtığını bilersek ayarlanmış bir işarettir vez
bir bayrak kümesi bilinen herhangi bir karakterden daha erken bir görünüme sahip bir karakter bulursak bir bayrak kümesidir. Geçerli karakterlerin en eski görünümlerix
. Bu fikrin mevcut uygulaması biraz dağınık, ancak birçok durumda performansı neredeyse iki katına çıkardı.
Bu iki fikirle, bir saat içinde bitmeyen şey yaklaşık 0.015 saniye sürdü.
Muhtemelen performansı hızlandıracak çok daha küçük püf noktaları var, ancak bu noktada her şeyi golf oynama yeteneğim hakkında endişelenmeye başladım. Ben hala golf memnun değilim, bu yüzden muhtemelen daha sonra geri gelecektir!
zamanlamalar
Sizi çevrimiçi denemeye davet ettiğim bazı test kodları :
#include "stdio.h"
#include "time.h"
#define SIZE_ARRAY(x) (sizeof(x) / sizeof(*x))
int main(int argc, char** argv) {
/* Our test case */
char* test7[] = {
"nqrualgoedlf",
"jgqorzglfnpa",
"fgttvnogldfx",
"pgostsulyfug",
"sgnhoyjlnfvr",
"wdttgkolfkbt"
};
printf("Test 7:\n\t");
clock_t start = clock();
/* The call to L */
int size = L(test7, SIZE_ARRAY(test7));
double dt = ((double)(clock() - start)) / CLOCKS_PER_SEC;
printf("\tSize: %d\n", size);
printf("\tElapsed time: %lf s\n", dt);
return 0;
}
OP'nin test senaryolarını 1,7 GHz Intel Core i7 çip ile donatılmış bir dizüstü bilgisayarda, optimizasyon ayarı ile çalıştırdım -Ofast
. Simülasyon, 712KB'lik bir pik gerekli olduğunu bildirdi. Her test vakasının zamanlamaları olan bir örnek çalışması:
Test 1:
a
Size: 1
Elapsed time: 0.000020 s
Test 2:
x
Size: 1
Elapsed time: 0.000017 s
Test 3:
hecbpyhogntqppcqgkxchpsieuhbmcbhuqdjbrqmclchqyfhtdvdoysuhrrl
Size: 60
Elapsed time: 0.054547 s
Test 4:
ihicvaoodsnktkrar
Size: 17
Elapsed time: 0.007459 s
Test 5:
krkk
Size: 4
Elapsed time: 0.000051 s
Test 6:
code
Size: 4
Elapsed time: 0.000045 s
Test 7:
golf
Size: 4
Elapsed time: 0.000040 s
Test 8:
Size: 0
Elapsed time: 0.000029 s
Total time: 0.062293 s
Golfte, performansı oldukça önemli bir şekilde vurdum ve insanlar önceki 618 baytlık çözümümün kaba hızını (tüm test senaryolarını tamamlamak için 0.013624 s) beğenmiş gibi göründüğünden, referans için burada bırakacağım:
d,M,N,A[9999][2];char*(R[9999][20]),b[1000];L(char**s,n){char*j[20],c,a=0;int x[n],y,z,i,t,m=0,w=1;for(y=0;y<n;y++)x[y]=999;for(y=0;y<N;y++){for(i=0;i<n;i++)if(s[i]!=R[y][i])break;if(i==n){a=A[y][0];m=A[y][1];w=0;if(m+d<M||!a)goto J;else{c=a;goto K;}}}for(c=97;w&&c<'{';c++){K:t=1,y=1,z=1;for(i=0;i<n;j[i++]++){for(j[i]=s[i];*j[i]-c;j[i]++)if(!*j[i]){t=0;goto I;}if(j[i]-s[i]>x[i])z=0;if(j[i]-s[i]<x[i])y=0;}if(y){t=0;}I:if(t){if(z){for(i=0;i<n;i++){x[i]=j[i]-s[i];}}d++,t+=L(j,n),d--,m=t>m?(a=c),t:m;}}if(w){for(y=0;y<n;y++)R[N][y]=s[y];A[N][0]=a;A[N++][1]=m;}J:if(d+m>=M)M=d+m,b[d]=a;if(!d)N=0,M=0,puts(b);return m;}
Algoritmanın kendisi değişmez, ancak yeni kod bölümlere ve her şeyi yavaşlatan bazı daha karmaşık bitsel işlemlere dayanır.