Hepsi bir arada maçları bulma


18

Bu sorun, aşağıdaki sorunu çözmek için kod yazmakla ilgilidir.

İki A ve B dizesi verildiğinde, kodunuz A'nın bir alt dizesinin başlangıç ​​ve bitiş indekslerini aşağıdaki özelliklere sahip olarak vermelidir.

  • A'nın alt dizesi, B'nin bazı alt dizgesini dizede tek bir karakterin bir ikamesi ile eşleştirmelidir.
  • Artık A'nın ilk özelliğini karşılayan alt dizesi olmamalıdır.

Örneğin:

A = xxxappleyyyyyyy

B = zapllezzz

Substring appleindisli 4 8(1 indeksleme) geçerli bir çıktı olacaktır.

Puan

Cevabınızın puanı, bayt cinsinden kodunuzun uzunluğunun toplamı + her biri 1 milyon uzunluğunda A ve B dizelerinde çalıştırıldığında bilgisayarımdaki saniye cinsinden süredir.

Test ve giriş

Kodunuzu http://hgdownload.cse.ucsc.edu/goldenPath/hg38/chromosomes/ adresindeki dizelerden alınan 1 milyon uzunluğunda iki dizede çalıştıracağım.

Giriş standart girişte olacak ve yeni bir satırla ayrılmış iki dize olacaktır.

Diller ve kütüphaneler

Serbestçe kullanılabilen bir derleyici / tercüman / vb. Olan herhangi bir dili kullanabilirsiniz. Linux ve açık kaynak kodlu olan ve Linux için serbestçe kullanılabilen tüm kütüphaneler için.

Benim makine zamanlamaları benim makinede işletilecek. Bu, AMD FX-8350 Sekiz Çekirdekli İşlemciye standart bir ubuntu yüklemesidir. Bu ayrıca kodunuzu çalıştırabilmem gerektiği anlamına gelir. Sonuç olarak, yalnızca kolayca bulunabilen ücretsiz yazılımları kullanın ve lütfen kodunuzu nasıl derleyeceğiniz ve çalıştıracağınızla ilgili tüm talimatları ekleyin.


Daha mutlak puanlama tanımına ihtiyacınız var. Bilgisayarınızda çalışma süresi iyi bir puanlama yöntemi gibi gelmiyor.
mbomb007

7
@ mbomb007 Kod hızını ölçmenin tek mantıklı yolu ve PPCG'deki en hızlı kod yarışmalarında her zaman kullanılan yöntemdir! İnsanlar normalde kendi cevaplarını kendi bilgisayarlarına cevaplarında yazarlar ve OP'nin kesin bir puan vermesini beklerler. En azından% 100 açık.

5
@ mbomb007 en hızlı kod için çok kullanılan bir puanlama yöntemidir.
Doktor

if(hash(str1 == test1 && str2 == test2)) print("100,150") else ..- düşünceler?
John Dvorak

2
@FryAmTheEggman Çok az olası bir beraberlikte ilk cevap kazanır. appleyeşleşmesi için iki oyuncu değişikliğine ihtiyaç vardır apllez. Belki de apllB'de değil de kaçırdınız applmı?

Yanıtlar:


4

C ++ süresi: O (n ^ 2), fazladan boşluk: O (1)

Makinemdeki 15K verilerinin tamamlanması 0.2s alır.

Derlemek için şunu kullanın:

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

Çalıştırmak için şunu kullanın:

./code < INPUT_FILE_THAT_CONTAINS_TWO_LINES_SPERATED_BY_A_LINE_BREAK

Açıklama

Fikir basit, dize için s1ve s2biz s2tarafından dengelemek için çalışıyoruz i:

s1: abcabcabc
s2: bcabcab

Ofset 3 olduğunda:

s1: abcabcabc
s2:    bcabcab

Ardından, her bir ofset iiçin s1[i:]ve üzerinde dinamik bir programlama taraması yaparız s2. Her biri için j, Let f[j, 0]maksimum uzunluğu döyle ki s1[j - d:j] == s2[j - i - d: j - i]. Benzer şekilde, dizeler ve en fazla 1 karakter farklı f[j, 1]olacak şekilde maksimum uzunluk olsun .ds1[j - d:j]s2[j - i - d:j - i]

Yani s1[j] == s2[j - i], biz var:

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]

Aksi takdirde:

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])

Ve:

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

F [j,:] değerini hesaplamak için sadece f [j - 1,:] ihtiyacımız olduğundan, yalnızca O (1) fazladan boşluk kullanılır.

Son olarak, maksimum uzunluk:

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

kod

#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;
}

Üzgünüz, koda bakıyordum ve nasıl örnek "elma" ve "aplle" gibi, bir karakter dışında eşleşen dizeleri olasılığı dikkate nasıl bulamıyorum. Açıklayabilir misiniz?
rorlork

@rcrmn Dinamik programlama bölümünün yaptığı şey budur. Anlamak için, bazı basit durumlar için f [j, 0] ve f [j, 1] 'i manuel olarak hesaplamayı denemek faydalıdır. Önceki kod bazı hatalar var, bu yüzden yazı güncelledi.
Ray

Bunun için teşekkür ederim. Sizce O (n log n) çözümü de olabilir mi?

2

C ++

Bunu yapmak için iyi bir algoritma düşünmeye çalıştım, ama bugün biraz dikkatim dağıldı ve işe yarayacak hiçbir şey düşünemedim. Bu O (n ^ 3) zamanında çalışır, bu nedenle veeeery yavaş. Düşündüğüm diğer seçenek teorik olarak daha hızlı olabilirdi ama O (n ^ 2) alanı kaplardı ve 1M girişleri ile daha da kötü olurdu.

Utanç verici, 15K girişler için 190 saniye sürüyor. Onu geliştirmeye çalışacağım. Düzenleme: Çok işlem eklendi. Şimdi 8 iş parçacığında 15K giriş için 37 saniye sürüyor.

#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;
}

Gerçekten çok kötü bir çözüm olduğu için üzgünüm. Bunu daha iyi bir zamanda başarmak için bir algoritma arıyordum, ama şimdilik bir şey bulamadım ...
rorlork

Gerekli görevin karmaşıklığı O (n ^ 4) ila O (n ^ 5) civarında olmalıdır, bu nedenle uzun çalışma süreleri verilir
hoffmale

En azından algoritma ile en kötü durumda O (n ^ 3) gibi olması gerektiğine inanıyorum. Her neyse, bir tür ağaç araması gibi, onu iyileştirmek için bir şey yapılabileceğinden eminim, ancak nasıl uygulanacağından emin değilim.
rorlork

Oh evet, O (n ^ 3) ... O (n ^ 4) '
ü

Eğer gelen döngüler için iki dış içinde onay değiştirirseniz zaman az miktarda kurtarabilecek i < a.length()için i < a.length - (longestA.second - longestA.first)şu anki en uzun dışında herhangi maçlar küçük işlemek gerekmez çünkü (j ve b.length () için aynı)
hoffmale

2

R,

Sorunu önceki çözümle daha fazla karmaşıklaştırdığımı görünüyor. Bu, bir öncekinden yaklaşık% 50 daha hızlıdır (15k dizelerde 23 saniye) ve oldukça basittir.

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)

Bu dil nedeniyle asla bir yarışmacı olmayacak, ama bunu yaparken biraz eğlendim.
Karmaşık olduğundan emin değilim, ancak ~ 15k dizelerin birkaç üzerinde tek bir iplik kullanarak 43 saniye sürer. Bunun en büyük kısmı dizilerin sıralanmasıydı. Başka kütüphaneler denedim ama önemli bir gelişme olmadı.

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)

Yöntem:

  • Her dize için bir sonek dizisi oluşturma
  • Sonek dizilerini sipariş etme
  • Dizilerin her birini, her birinin başlangıcını karşılaştıran şaşırtıcı bir şekilde adımlayın

Tabii ki, R'deki en kolay çözüm Bioiletken kullanmaktır.
archaephyrryx

@archaephyrryx Bir biyoiletken çözüm eğlenceli olurdu.

Ama ... Belgeleri hızlıca okumam başımın üstündeydi. Belki de terimleri
anlarsam

İlk yorumumu sildim. Elbette bu meydan okuma için istediğiniz herhangi bir açık kaynak kütüphanesini kullanabilirsiniz.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.