Tìm tất cả các trận đấu


18

Thách thức này là về viết mã để giải quyết vấn đề sau.

Cho hai chuỗi A và B, mã của bạn sẽ xuất ra các chỉ số bắt đầu và kết thúc của một chuỗi con của A với các thuộc tính sau.

  • Chuỗi con của A cũng phải khớp với một số chuỗi con của B với tối đa một thay thế một ký tự trong chuỗi.
  • Không nên có chuỗi con của A thỏa mãn thuộc tính đầu tiên.

Ví dụ:

A = xxxappleyyyyyyy

B = zapllezzz

Chuỗi con applecó chỉ số 4 8(lập chỉ mục từ 1) sẽ là đầu ra hợp lệ.

Ghi bàn

Điểm của câu trả lời của bạn sẽ là tổng độ dài mã của bạn tính bằng byte + thời gian tính bằng giây trên máy tính của tôi khi chạy trên chuỗi A và B có độ dài 1 triệu mỗi chuỗi.

Kiểm tra và đầu vào

Tôi sẽ chạy mã của bạn trên hai chuỗi có độ dài 1 triệu được lấy từ các chuỗi trong http: // hgdoad.cse.ucsc.edu/goldenPath/hg38/chromosomes/

Đầu vào sẽ ở trên tiêu chuẩn và đơn giản là hai chuỗi, cách nhau bởi một dòng mới.

Ngôn ngữ và thư viện

Bạn có thể sử dụng bất kỳ ngôn ngữ nào có trình biên dịch / trình thông dịch / có sẵn miễn phí. cho Linux và bất kỳ thư viện nào cũng là nguồn mở và có sẵn miễn phí cho Linux.

Máy của tôi Thời gian sẽ được chạy trên máy của tôi. Đây là bản cài đặt Ubuntu tiêu chuẩn trên Bộ xử lý tám lõi AMD FX-8350. Điều này cũng có nghĩa là tôi cần để có thể chạy mã của bạn. Do đó, chỉ sử dụng phần mềm miễn phí có sẵn dễ dàng và vui lòng bao gồm các hướng dẫn đầy đủ về cách biên dịch và chạy mã của bạn.


Bạn cần định nghĩa ghi điểm tuyệt đối hơn. Thời gian chạy trên máy tính của bạn không giống như một phương pháp cho điểm tốt.
mbomb007 27/2/2015

7
@ mbomb007 Đây là cách hợp lý duy nhất để đo tốc độ mã và là cách luôn được sử dụng trong các cuộc thi mã nhanh nhất trên PPCG! Mọi người thường đăng điểm số của họ trên máy tính của họ trong câu trả lời của họ và chờ OP sau đó tạo ra một số điểm dứt khoát. Nó ít nhất 100% rõ ràng.

5
@ mbomb007 là một phương pháp tính điểm được sử dụng rất rộng rãi cho mã nhanh nhất.
Tối ưu hóa

if(hash(str1 == test1 && str2 == test2)) print("100,150") else ..- ý nghĩ?
John Dvorak

2
@FryAmTheEggman Trong trường hợp rất khó xảy ra của một chiếc cà vạt, câu trả lời đầu tiên sẽ thắng. appleycần hai sự thay thế để phù hợp apllez. Có lẽ bạn đã bỏ lỡ rằng nó là aplltrong B và không appl?

Câu trả lời:


4

Thời gian C ++: O (n ^ 2), không gian thừa: O (1)

Phải mất 0,2 giây để hoàn thành dữ liệu 15K trên máy của tôi.

Để biên dịch nó, sử dụng:

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

Để chạy nó, sử dụng:

./code < INPUT_FILE_THAT_CONTAINS_TWO_LINES_SPERATED_BY_A_LINE_BREAK

Giải thích

Ý tưởng là đơn giản, cho chuỗi s1s2, chúng tôi cố gắng bù đắp s2bằng i:

s1: abcabcabc
s2: bcabcab

Khi bù là 3:

s1: abcabcabc
s2:    bcabcab

Sau đó, với mỗi phần bù i, chúng tôi thực hiện quét chương trình động s1[i:]s2. Đối với mỗi j, hãy để f[j, 0]độ dài tối đa dsao chos1[j - d:j] == s2[j - i - d: j - i] . Tương tự, hãy để f[j, 1]độ dài tối đa dsao cho các chuỗi s1[j - d:j]s2[j - i - d:j - i]khác nhau tối đa 1 ký tự.

Vì vậy s1[j] == s2[j - i], chúng tôi có:

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]

Nếu không thì:

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

Và:

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

Vì chúng ta chỉ cần f [j - 1 ,:] để tính f [j ,:], nên chỉ sử dụng thêm không gian O (1).

Cuối cùng, độ dài tối đa sẽ là:

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

Xin lỗi, tôi đã xem mã và tôi không thể tìm ra cách tính đến khả năng khớp chuỗi ngoại trừ một ký tự, như trong ví dụ "apple" và "aplle". Bạn có thể giải thích?
rorlork

@rcrmn Đó là những gì phần lập trình động đang làm. Để hiểu, thật hữu ích khi thử tính toán f [j, 0] và f [j, 1] bằng tay cho một số trường hợp đơn giản. Đoạn mã trước có một số lỗi nên tôi đã cập nhật bài viết.
Ray

Cảm ơn vì điều này. Bạn có nghĩ rằng có thể có một giải pháp O (n log n) không?

2

C ++

Tôi đã cố gắng nghĩ ra một thuật toán tốt để làm điều này, nhưng hôm nay tôi hơi mất tập trung và không thể nghĩ ra bất cứ điều gì sẽ hoạt động tốt. Thời gian này chạy ở thời điểm O (n ^ 3), vì vậy nó rất chậm. Tùy chọn khác mà tôi nghĩ có thể nhanh hơn về mặt lý thuyết nhưng sẽ chiếm không gian O (n ^ 2) và với đầu vào là 1M, nó thậm chí còn tệ hơn.

Thật đáng xấu hổ, phải mất 190 giây cho đầu vào 15K. Tôi sẽ cố gắng cải thiện nó. Chỉnh sửa: Đã thêm đa xử lý. Bây giờ mất 37 giây cho đầu vào 15K trên 8 luồng.

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

Tôi thực sự xin lỗi đó là một giải pháp tồi. Tôi đã tìm kiếm một thuật toán để thực hiện điều này trong thời gian tốt hơn, nhưng hiện tại tôi không tìm thấy bất cứ điều gì ...
rorlork

Chà, độ phức tạp của nhiệm vụ cần phải ở khoảng O (n ^ 4) đến O (n ^ 5), vì vậy thời gian chạy dài là một thời gian nhất định
hoffmale

Tôi tin rằng nó sẽ giống O (n ^ 3) hơn trong trường hợp xấu nhất, ít nhất là với thuật toán của tôi. Dù sao, tôi chắc chắn một cái gì đó có thể được thực hiện để cải thiện nó, như một số loại tìm kiếm cây, nhưng tôi không chắc nó sẽ được thực hiện như thế nào.
rorlork

Ồ vâng, O (n ^ 3), đó là ... có một cách tiếp cận khác trong tâm trí sẽ lấy O (n ^ 4), nhưng bây giờ người ta vô dụng với xD
hoffmale

bạn có thể tiết kiệm một lượng nhỏ thời gian nếu bạn thay đổi dấu check ở mục hai bên ngoài cho vòng từ i < a.length()đến i < a.length - (longestA.second - longestA.first)(tương tự cho j và b.length ()) vì bạn sẽ không cần phải xử lý bất kỳ trận đấu nhỏ hơn so với hiện tại dài nhất của bạn
hoffmale

2

R

Có vẻ như tôi đã quá phức tạp hóa vấn đề với giải pháp trước đó. Tốc độ này nhanh hơn khoảng 50% (23 giây trên chuỗi 15k) so với chuỗi trước và khá đơn giản.

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)

Điều này sẽ không bao giờ là một ứng cử viên do ngôn ngữ, nhưng tôi đã có một chút thú vị khi làm điều đó.
Không chắc về độ phức tạp của nó, nhưng qua một vài chuỗi ~ 15k, phải mất 43 giây bằng một luồng. Phần lớn nhất trong số đó là việc sắp xếp các mảng. Tôi đã thử một số thư viện khác, nhưng không có cải thiện đáng kể.

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)

Phương pháp:

  • Tạo một mảng hậu tố cho mỗi chuỗi
  • Sắp xếp các mảng hậu tố
  • Bước qua từng mảng theo cách so le so sánh điểm bắt đầu của từng mảng

Tất nhiên, giải pháp đơn giản nhất trong R là sử dụng Bioconductor.
archaephyrryx 04/03/2015

@archaephyrryx Một giải pháp chất dẫn sinh học sẽ rất vui.

Nó sẽ là ... Nhưng việc đọc nhanh các tài liệu của tôi đã vượt qua đầu tôi. Có lẽ nếu tôi hiểu các điều khoản :-)
MickyT

Tôi đã xóa bình luận đầu tiên của tôi. Tất nhiên bạn có thể sử dụng bất kỳ thư viện nguồn mở nào bạn thích cho thử thách này.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.