सी, 618 564 बाइट्स
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;}
और यहाँ यह "पठनीयता" के लिए अप्रकाशित है:
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;
}
देवियों और सज्जनों, मैंने एक भयानक गलती की है। यह प्रयोग किया जाता है खूबसूरत होने के लिए ... और गोटो कम ... अब कम से कम यह है तेजी से ।
हम एक पुनरावर्ती फ़ंक्शन को परिभाषित करते हैं Lजो इनपुट sके वर्णों की एक सरणी और nस्ट्रिंग की संख्या के रूप में लेता है । फ़ंक्शन परिणामी स्ट्रिंग को स्टडआउट करने के लिए आउटपुट करता है, और संयोग से उस स्ट्रिंग के पात्रों में आकार देता है।
पहुंच
हालाँकि यह कोड जटिल है, लेकिन यहां रणनीति बहुत जटिल नहीं है। हम एक बल्कि भोली पुनरावर्ती एल्गोरिथ्म के साथ शुरू करते हैं, जिसे मैं छद्मकोड के साथ वर्णित करूंगा:
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
अब, यह एल्गोरिथ्म अपने आप में बहुत नृशंस है (लेकिन लगभग 230 बाइट्स में फिट किया जा सकता है, मैंने पाया है)। यह नहीं है कि कैसे तेजी से परिणाम प्राप्त होता है। यह एल्गोरिथ्म स्ट्रिंग की लंबाई के साथ अविश्वसनीय रूप से खराब होता है। यह एल्गोरिथ्म , हालांकि, बड़ी संख्या में तारों के साथ काफी अच्छी तरह से स्केल करता है । अंतिम परीक्षण का मामला लगभग तुरंत हल हो जाएगा, क्योंकि sकिसी भी तार में कोई भी चरित्र नहीं cहै। दो मुख्य चालें हैं जिन्हें मैंने ऊपर लागू किया है जिसके परिणामस्वरूप एक अविश्वसनीय गति में वृद्धि हुई है:
हर कॉल पर L, जांचें कि क्या हमें पहले भी यही इनपुट दिया गया है। चूँकि अभ्यास सूचनाओं को संकेत के माध्यम से तार के एक ही सेट पर भेजा जाता है, इसलिए हमें वास्तव में तार की तुलना नहीं करनी पड़ती है, बस स्थान, जो बहुत अच्छा है। यदि हमें पता चलता है कि हमने यह जानकारी पहले प्राप्त कर ली है, तो गणना के माध्यम से चलने की कोई आवश्यकता नहीं है (ज्यादातर समय, लेकिन आउटपुट प्राप्त करना इसे थोड़ा अधिक जटिल बनाता है) और हम सिर्फ लंबाई वापस करने के साथ दूर हो सकते हैं। यदि हमें कोई मेल नहीं मिलता है, तो भविष्य के कॉल की तुलना करने के लिए इनपुट / आउटपुट के इस सेट को सहेजें। सी कोड में, दूसरा forलूप इनपुट से मिलान खोजने का प्रयास करता है। ज्ञात इनपुट पॉइंटर्स में सहेजे गए हैं R, और संबंधित लंबाई और चरित्र आउटपुट मानों में संग्रहीत हैंA। इस योजना का रनटाइम पर काफी प्रभाव था, खासकर लंबे समय तक तार के साथ।
हर बार जब हम के स्थान ढूंढने cमें s, इस बात की संभावना है कि हम सही बल्ले से दूर जानते हैं कि क्या हमने पाया है इष्टतम नहीं है। यदि प्रत्येक स्थान किसी अन्य पत्र के कुछ ज्ञात स्थान के बादc दिखाई देता है, तो हम स्वचालित रूप से जानते हैं कि इससे एक इष्टतम विकल्प नहीं बनता है, क्योंकि आप इसमें एक और अक्षर फिट कर सकते हैं। इसका मतलब है कि एक छोटी सी लागत के लिए, हम संभावित रूप से बड़े तार के लिए कई सौ कॉल निकाल सकते हैं। उपरोक्त सी कोड में, एक ध्वज सेट है यदि हम स्वचालित रूप से जानते हैं कि यह चरित्र एक सबॉप्टिमल स्ट्रिंग की ओर जाता है, और एक ध्वज सेट है यदि हमें एक ऐसा चरित्र मिलता है जो किसी अन्य ज्ञात चरित्र की तुलना में विशेष रूप से पहले दिखाई देता है। वर्णों की वर्तमान प्रारंभिक अभिव्यक्तियों को संग्रहीत किया जाता हैcLyzx। इस विचार का वर्तमान कार्यान्वयन थोड़ा गड़बड़ है, लेकिन कई उदाहरणों में लगभग दोगुना है।
इन दो विचारों के साथ, एक घंटे में खत्म नहीं हुआ अब लगभग 0.015 सेकंड लग गए।
संभवतः बहुत अधिक छोटी चालें हैं जो प्रदर्शन को गति दे सकती हैं, लेकिन इस बिंदु पर मुझे सब कुछ गोल्फ की अपनी क्षमता के बारे में चिंता करना शुरू कर दिया। मैं अभी भी गोल्फ से संतुष्ट नहीं हूँ, इसलिए मैं बाद में इस पर वापस आऊंगा!
समय
यहां कुछ परीक्षण कोड दिए गए हैं, जिन्हें मैं आपको ऑनलाइन प्रयास करने के लिए आमंत्रित करता हूं :
#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;
}
मैंने एक ऑप्टिमाइज़ेशन सेटिंग के साथ 1.7 गीगाहर्ट्ज इंटेल कोर आई 7 चिप से लैस लैपटॉप पर ओपी के परीक्षण के मामलों को चलाया -Ofast। सिमुलेशन ने 712KB के एक शिखर की आवश्यकता की सूचना दी। यहाँ प्रत्येक परीक्षण मामले का एक उदाहरण रन है, समय के साथ:
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
गोल्फिंग में, मैंने प्रदर्शन को काफी प्रभावित किया, और जब से लोगों को मेरे पिछले 618-बाइट समाधान के ब्रूट गति (0.013624 के सभी परीक्षण मामलों को संयुक्त रूप से पूरा करना) पसंद आया, मैं इसे संदर्भ के लिए यहां छोड़ दूंगा:
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;}
एल्गोरिथ्म स्वयं अपरिवर्तित है, लेकिन नया कोड डिवीजनों और कुछ पेचीदा बिटवाइज़ ऑपरेशनों पर निर्भर करता है जो अंत में पूरी चीज़ को धीमा कर देते हैं।