ऑल-बट-वन मैच ढूंढना


18

यह चुनौती निम्नलिखित समस्या को हल करने के लिए कोड लिखने के बारे में है।

दो तार ए और बी को देखते हुए, आपके कोड को निम्नलिखित गुणों के साथ ए के विकल्प के प्रारंभ और अंत सूचक का उत्पादन करना चाहिए।

  • A के विकल्प को स्ट्रिंग में एकल वर्ण के एक प्रतिस्थापन के साथ B के कुछ विकल्प से भी मेल खाना चाहिए।
  • ए का कोई विकल्प नहीं होना चाहिए जो पहली संपत्ति को संतुष्ट करता है।

उदाहरण के लिए:

A = xxxappleyyyyyyy

B = zapllezzz

सबस्ट्रिंग appleसूचकांक के साथ 4 8(1 से का अनुक्रमण) एक वैध उत्पादन होगा।

स्कोर

आपके उत्तर का स्कोर बाइट्स में आपके कोड की लंबाई का योग होगा + सेकंड में वह समय जब यह मेरे कंप्यूटर पर लेता है जब स्ट्रिंग्स A और B की लंबाई 1 मिलियन होती है।

परीक्षण और इनपुट

मैं http://hgdownload.cse.ucsc.edu/goldenPath/hg38/chromosomes/ में तार से ली गई लंबाई के दो तारों पर आपका कोड चलाऊंगा

इनपुट मानक पर होगा और एक नई लाइन द्वारा अलग किए गए दो तार होंगे।

भाषा और पुस्तकालय

आप किसी भी भाषा का उपयोग कर सकते हैं जिसमें एक स्वतंत्र रूप से उपलब्ध संकलक / दुभाषिया / आदि है। लिनक्स और किसी भी पुस्तकालय के लिए जो खुला स्रोत भी हैं और लिनक्स के लिए स्वतंत्र रूप से उपलब्ध हैं।

मेरी मशीन टाइमिंग मेरी मशीन पर चलाई जाएगी। यह एक मानक ubuntu एक AMD FX-8350 आठ-कोर प्रोसेसर पर स्थापित है। इसका मतलब यह भी है कि मुझे आपका कोड चलाने में सक्षम होना चाहिए। परिणामस्वरूप, केवल आसानी से उपलब्ध मुफ्त सॉफ़्टवेयर का उपयोग करें और कृपया अपने कोड को संकलित करने और चलाने के लिए पूर्ण निर्देश शामिल करें।


आपको अधिक पूर्ण स्कोरिंग परिभाषा की आवश्यकता है। आपके कंप्यूटर पर चलने का समय एक अच्छी स्कोरिंग पद्धति की तरह नहीं है।
mbomb007

7
@ mbomb007 यह कोड की गति को मापने का एकमात्र समझदार तरीका है और पीपीसीजी पर सबसे तेज़ कोड प्रतियोगिताओं में हमेशा उपयोग किया जाता है! लोग आमतौर पर अपने कंप्यूटर पर अपने उत्तर में अपना स्कोर पोस्ट करते हैं और ओपी का इंतजार करते हैं और फिर एक निश्चित स्कोर का निर्माण करते हैं। यह कम से कम 100% असंदिग्ध है।

5
@ mbomb007 जो सबसे तेज़ कोड के लिए एक बहुत व्यापक रूप से उपयोग की जाने वाली स्कोरिंग विधि है।
ऑप्टिमाइज़र

if(hash(str1 == test1 && str2 == test2)) print("100,150") else ..- विचार?
जॉन ड्वोरक

2
@FryAmTheEggman एक टाई की बहुत ही अप्रत्याशित घटना में, पहला उत्तर जीतता है। appleyमैच के लिए दो प्रतिस्थापन की आवश्यकता है apllez। शायद आप चूक गए कि यह apllबी में है और नहीं appl?

जवाबों:


4

C ++ समय: O (n ^ 2), अतिरिक्त स्थान: O (1)

मेरी मशीन पर 15K डेटा को पूरा करने में 0.2 सेकंड लगते हैं।

इसे संकलित करने के लिए, उपयोग करें:

g++ -std=c++11 -O3 code.cpp -o code

इसे चलाने के लिए, उपयोग करें:

./code < INPUT_FILE_THAT_CONTAINS_TWO_LINES_SPERATED_BY_A_LINE_BREAK

explaination

यह विचार सरल है, स्ट्रिंग के लिए s1और s2, हम इसके s2द्वारा ऑफसेट करने का प्रयास करते हैं i:

s1: abcabcabc
s2: bcabcab

जब ऑफसेट 3 है:

s1: abcabcabc
s2:    bcabcab

फिर, प्रत्येक ऑफसेट के लिए i, हम एक गतिशील प्रोग्रामिंग स्कैन ऑन s1[i:]और करते हैं s2। प्रत्येक के लिए j, f[j, 0]अधिकतम लंबाई dऐसी हो s1[j - d:j] == s2[j - i - d: j - i]। इसी तरह, f[j, 1]अधिकतम लंबाई dइस तरह की हो कि स्ट्रिंग्स s1[j - d:j]और s2[j - i - d:j - i]सबसे अधिक 1 वर्ण से भिन्न हो।

तो s1[j] == s2[j - i], हमारे पास:

f[j, 0] = f[j - 1, 0] + 1  // concat solution in f[j - 1, 0] and s1[j]
f[j, 1] = f[j - 1, 1] + 1  // concat solution in f[j - 1, 1] and s1[j]

अन्यथा:

f[j, 0] = 0  // the only choice is empty string
f[j, 1] = f[j - 1, 0] + 1  // concat solution in f[j - 1, 0] and s1[j] (or s2[j - i])

तथा:

f[-1, 0] = f[-1, 1] = 0 

चूँकि हमें केवल f [j,:] की गणना करने के लिए f [j - 1,:] की आवश्यकता है, केवल O (1) अतिरिक्त स्थान का उपयोग किया जाता है।

अंत में, अधिकतम लंबाई होगी:

max(f[j, 1] for all valid j and all i)

कोड

#include <string>
#include <cassert>
#include <iostream>

using namespace std;

int main() {
    string s1, s2;
    getline(cin, s1);
    getline(cin, s2);
    int n1, n2;
    n1 = s1.size();
    n2 = s2.size();
    int max_len = 0;
    int max_end = -1;
    for(int i = 1 - n2; i < n1; i++) {
        int f0, f1;
        int max_len2 = 0;
        int max_end2 = -1;
        f0 = f1 = 0;
        for(int j = max(i, 0), j_end = min(n1, i + n2); j < j_end; j++) {
            if(s1[j] == s2[j - i]) {
                f0 += 1;
                f1 += 1;
            } else {
                f1 = f0 + 1;
                f0 = 0;
            }
            if(f1 > max_len2) {
                max_len2 = f1;
                max_end2 = j + 1;
            }
        }
        if(max_len2 > max_len) {
            max_len = max_len2;
            max_end = max_end2;
        }
    }
    assert(max_end != -1);
    // cout << max_len << endl;
    cout << max_end - max_len + 1 << " " << max_end << endl;
}

क्षमा करें, मैं कोड देख रहा हूं और मुझे नहीं पता कि यह कैसे एक चरित्र को छोड़कर स्ट्रिंग की संभावना को ध्यान में रखता है, उदाहरण के लिए "ऐप्पल" और "एपल"। क्या आप समझाएँगे?
रोरलर्क

@ आर्कन, गतिशील प्रोग्रामिंग भाग क्या कर रहा है। समझने के लिए, कुछ सरल मामलों के लिए मैन्युअल रूप से f [j, 0] और f [j, 1] की गणना करने की कोशिश करना मददगार है। पिछले कोड में कुछ बग हैं इसलिए मैंने पोस्ट को अपडेट किया।
रे

इसके लिए शुक्रिया। क्या आपको लगता है कि कोई ओ (एन लॉग एन) समाधान भी हो सकता है?

2

सी ++

मैंने ऐसा करने के लिए एक अच्छे एल्गोरिथ्म के बारे में सोचने की कोशिश की, लेकिन मैं आज थोड़ा विचलित हूं और ऐसा कुछ भी नहीं सोच सकता जो अच्छा काम करे। यह O (n ^ 3) समय पर चलता है, इसलिए यह धीमी गति से चलने वाला है। मेरे द्वारा सोचा गया दूसरा विकल्प सैद्धांतिक रूप से तेज़ हो सकता था, लेकिन O (n ^ 2) स्थान ले सकता था, और 1M के इनपुट के साथ, यह और भी बुरा होता।

यह शर्मनाक है, 15K के इनपुट के लिए 190 सेकंड लगते हैं। मैं इसे सुधारने की कोशिश करूंगा। संपादित करें: जोड़ा गया मल्टीप्रोसेसिंग। अब 8 थ्रेड्स पर 15K इनपुट के लिए 37 सेकंड लगते हैं।

#include <string>
#include <vector>
#include <sstream>
#include <chrono>
#include <thread>
#include <atomic>
#undef cin
#undef cout
#include <iostream>

using namespace std;

typedef pair<int, int> range;

int main(int argc, char ** argv)
{
    string a = "xxxappleyyyyyyy";
    string b = "zapllezzz";

    getline(cin, a);
    getline(cin, b);

    range longestA;
    range longestB;

    using namespace std::chrono;

    high_resolution_clock::time_point t1 = high_resolution_clock::now();

    unsigned cores = thread::hardware_concurrency(); cores = cores > 0 ? cores : 1;

    cout << "Processing on " << cores << " cores." << endl;

    atomic<int> processedCount(0);

    vector<thread> threads;

    range* longestAs = new range[cores];
    range* longestBs = new range[cores];
    for (int t = 0; t < cores; ++t)
    {
        threads.push_back(thread([&processedCount, cores, t, &a, &b, &longestBs, &longestAs]()
        {
            int la = a.length();
            int l = la / cores + (t==cores-1? la % cores : 0);
            int lb = b.length();
            int aS = t*(la/cores);

            for (int i = aS; i < aS + l; ++i)
            {
                int count = processedCount.fetch_add(1);
                if ((count+1) * 100 / la > count * 100 / la)
                {
                    cout << (count+1) * 100 / la << "%" << endl;
                }
                for (int j = 0; j < lb; ++j)
                {
                    range currentB = make_pair(j, j);
                    bool letterChanged = false;
                    for (int k = 0; k + j < lb && k + i < la; ++k)
                    {
                        if (a[i + k] == b[j + k])
                        {
                            currentB = make_pair(j, j + k);
                        }
                        else if (!letterChanged)
                        {
                            letterChanged = true;
                            currentB = make_pair(j, j + k);
                        }
                        else
                        {
                            break;
                        }
                    }
                    if (currentB.second - currentB.first > longestBs[t].second - longestBs[t].first)
                    {
                        longestBs[t] = currentB;
                        longestAs[t] = make_pair(i, i + currentB.second - currentB.first);
                    }
                }
            }
        }));
    }

    longestA = make_pair(0,0);
    for(int t = 0; t < cores; ++t)
    {
        threads[t].join();

        if (longestAs[t].second - longestAs[t].first > longestA.second - longestA.first)
        {
            longestA = longestAs[t];
            longestB = longestBs[t];
        }
    }

    high_resolution_clock::time_point t2 = high_resolution_clock::now();

    duration<double> time_span = duration_cast<duration<double>>(t2 - t1);

    cout << "First substring at range (" << longestA.first << ", " << longestA.second << "):" << endl;
    cout << a.substr(longestA.first, longestA.second - longestA.first + 1) << endl;
    cout << "Second substring at range (" << longestB.first << ", " << longestB.second << "):" << endl;
    cout << b.substr(longestB.first, longestB.second - longestB.first + 1) << endl;
    cout << "It took me " << time_span.count() << " seconds for input lengths " << a.length() << " and " << b.length() <<"." << endl;

    char c;
    cin >> c;
    return 0;
}

मुझे खेद है कि यह इतना बुरा समाधान है। मैं बेहतर समय में इसे पूरा करने के लिए एक एल्गोरिथ्म की खोज कर रहा हूं, लेकिन अभी के लिए कुछ भी नहीं मिला ...
rorlork

ठीक है, आवश्यक कार्य की जटिलता O (n ^ 4) से O (n ^ 5) के आस-पास होनी चाहिए, इसलिए लंबे समय तक चलने वाले समय दिए गए हैं
hoffmale

मेरा मानना ​​है कि यह सबसे खराब स्थिति में O (n ^ 3) की तरह अधिक होना चाहिए, कम से कम मेरे एल्गोरिथ्म के साथ यह है। वैसे भी, मुझे यकीन है कि इसे सुधारने के लिए कुछ किया जा सकता है, जैसे किसी तरह की पेड़ खोज, लेकिन मुझे यकीन नहीं है कि इसे कैसे लागू किया जाएगा।
रॉलर्क

अरे हाँ, ओ (एन ^ 3) यह है ... मन में एक अलग दृष्टिकोण था जिसने ओ (एन ^ 4) लिया होगा, लेकिन वह अब तक xD द्वारा व्यर्थ है
hoffmale

अगर आप से छोरों के लिए दो बाहरी चेक-इन के बदलते हैं तो आप समय की एक छोटी राशि की बचत हो सकती i < a.length()करने i < a.length - (longestA.second - longestA.first)के बाद आप किसी भी मैचों की तुलना में अपने वर्तमान सबसे लंबे समय तक एक छोटे कार्रवाई करने के लिए की जरूरत नहीं होगी (जे और b.length () के लिए एक ही)
hoffmale

2

आर

लगता है कि मैं पिछले समाधान के साथ समस्या को जटिल कर रहा था। यह पिछले एक की तुलना में लगभग 50% तेज (15 सेकंड में 23 सेकंड) और बहुत सरल है।

rm(list=ls(all=TRUE))
a="xxxappleyyyyyyy"
b="zapllezzz"
s=proc.time()
matchLen=1
matchIndex=1
indexA = 1
repeat {    
    i = 0
    repeat {
        srch = substring(a,indexA,indexA+matchLen+i)
        if (agrepl(srch,b,max.distance=list(insertions=0,deletions=0,substitutions=1)))
            i = i + 1
        else {
            if (i > 0) {
                matchLen = matchLen + i - 1
                matchIndex = indexA
            }
            break
        }
    }
    indexA=indexA+1
    if (indexA + matchLen > nchar(a)) break
}
c(matchIndex, matchLen + matchIndex)
print (substring(a,matchIndex, matchLen + matchIndex))
print(proc.time()-s)

यह भाषा के कारण कभी भी दावेदार नहीं होगा, लेकिन मुझे इसे करने में थोड़ा मज़ा आया।
इसकी जटिलता के बारे में निश्चित नहीं है, लेकिन ~ 15k तार के एक जोड़े पर एक धागे का उपयोग करके 43 सेकंड लगते हैं। उस का सबसे बड़ा हिस्सा सरणियों की छंटाई था। मैंने कुछ अन्य पुस्तकालयों की कोशिश की, लेकिन महत्वपूर्ण सुधार के बिना।

a="xxxappleyyyyyyy"
b="zapllezzz"
s=proc.time()
N=nchar
S=substring
U=unlist
V=strsplit
A=N(a)
B=N(b)
a=S(a,1:A)
b=S(b,1:B)
a=sort(a,method="quick")
b=sort(b,method="quick")
print(proc.time()-s)
C=D=1
E=X=Y=I=0
repeat{
    if(N(a[C])>E && N(b[D])>E){
        for(i in E:min(N(a[C]),N(b[D]))){
            if (sum(U(V(S(a[C],1,i),''))==U(V(S(b[D],1,i),'')))>i-2){
                F=i
            } else break
        }
        if (F>E) {
            X=A-N(a[C])+1
            Y=X+F-1
            E=F
        }
        if (a[C]<b[D])
            C=C+1
            else
            D=D+1
    } else
        if(S(a[C],1,1)<S(b[D],1,1))C=C+1 else D=D+1
    if(C>A||D>B)break
}
c(X,Y)
print(proc.time()-s)

तरीका:

  • प्रत्येक स्ट्रिंग के लिए एक प्रत्यय सरणी बनाएँ
  • प्रत्यय सरण का आदेश दें
  • प्रत्येक की शुरुआत की तुलना करने के लिए एक तरह से कंपित प्रकार के सरणियों के माध्यम से कदम

बेशक, आर में सबसे आसान समाधान बायोकॉन्केटर का उपयोग करना है।
अर्चिफायरिक्स मार्क्स

@archaephyrryx एक बायोकॉन्टर समाधान मजेदार होगा।

यह होगा ... लेकिन डॉक्स का मेरा त्वरित तरीका मेरे सिर पर था। शायद अगर मैं शर्तों को समझ गया :-)
मिकी

मैंने अपनी पहली टिप्पणी हटा दी। आप निश्चित रूप से इस चुनौती के लिए अपनी पसंद के किसी भी ओपन सोर्स लाइब्रेरी का उपयोग कर सकते हैं।
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.