Doğrusal zamanda en uzun ortak alt dize


45

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

İki dizi A ve B verildiğinde, kodunuz A'nın bir alt dizinin başlangıç ​​ve bitiş indekslerini aşağıdaki özelliklerle vermelidir.

  • A'nın alt dizesi, B'nin bazı alt dizileriyle de eşleşmelidir.
  • İlk özelliği karşılayan A artık alt dize olmamalıdır.

Örneğin:

A = xxxappleyyyyyyy

B = zapplezzz

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

İşlevsellik

Girişin, yerel dizindeki bir dosyada ya da dosyada standart olacağını, yani seçiminizin olduğunu varsayabilirsiniz. Dosya formatı sadece iki satır olacak ve yeni bir satır ile ayrılacaktır. Cevap sadece bir fonksiyon değil, tam bir program olmalıdır.

Sonunda kodunuzu http://hgdownload.cse.ucsc.edu/goldenPath/hg38/chromosomes/ adresindeki dizelerden alınan iki alt dizgede test etmek istiyorum .

Puan

Bu bir bükülme ile kod golf. Kodunuz , girişin toplam uzunluğu O(n)nerede nise, zamanında çalıştırılmalıdır .

Diller ve kütüphaneler

Serbestçe kullanılabilen bir derleyici / tercüman / etc olan herhangi bir dili kullanabilirsiniz. Linux için. Yalnızca bu görevi çözmek için tasarlanmamış standart açık kaynak kitaplıkları kullanmalısınız. Anlaşmazlık durumunda, bu dili kendi dilinizle standart olarak gelen veya varsayılan bir depodan varsayılan bir ubuntu makinesine kurabileceğiniz herhangi bir kütüphane olarak sayacağım.

Kullanışlı bilgi

Bu problemi doğrusal zamanda çözmenin en az iki yolu vardır. Birincisi, sonek ağacını hesaplamak, ikincisi ise ilk sonek dizisini ve LCP dizisini hesaplamaktır.


4
O(n) timeMümkün olduğuna emin misin?
Savenkov Alexey,

17
@Lembik Üzgünüz, ama bunlar çok karmaşık algoritmalar ve 100'den fazla kod satırını çözmek gerçekten eğlenceli değil.
FUZxxl

4
"Faydalı bilgiler" altında sağladığınız ikinci bağlantıdaki makale "[bir sonek ağacı] oluşturmak için O (N ^ 2) zaman gerektiriyor" "
yazıyor

3
@Lembik Soruyu [en hızlı kod] olarak yapmalısınız, burada büyük oh notasyonunda en iyi senaryo olan programı kazanır. O zaman en azından bazı cevaplar alacaksınız ve birileri O (n) ' de çözebilecek olsa bile kazanacaklar.
mbomb007

9
Bu, geçerli cevap başına en çok silinen cevaplara sahip soru olmalı ...
FlipTack

Yanıtlar:


39

Python 2,646 bayt

G=range;w=raw_input;z=L,m,h=[0]*3
s=w();t=len(s);s+='!%s#'%w();u=len(s);I=z*u
def f(s,n):
 def r(o):
    b=[[]for _ in s];c=[]
    for x in B[:N]:b[s[x+o]]+=x,
    map(c.extend,b);B[:N]=c
 M=N=n--~n/3;t=n%3%2;B=G(n+t);del B[::3];r(2);u=m=p=r(1)>r(0);N-=n/3
 for x in B*1:v=s[x:x+3];m+=u<v;u=v;B[x/3+x%3/2*N]=m
 A=1/M*z or f(B+z,M)+z;B=[x*3for x in A if x<N];J=I[r(0):n];C=G(n)
 for k in C:b=A[t]/N;a=i,j=A[t]%N*3-~b,B[p];q=p<N<(s[i:i-~b],J[i/3+b+N-b*N])>(s[j+t/M:j-~b],J[j/3+b*N]);C[k]=x=a[q];I[x]=k;p+=q;t+=1-q
 return C
S=f(map(ord,s)+z*40,u)
for i in G(u):
 h-=h>0;j=S[I[i]-1]
 while s[i+h]==s[j+h]:h+=1
 if(i<t)==(t<j)<=h>m:m=h;L=min(i,j)
print-~L,L+m

Bu, Kärkkäinen ve Sanders tarafından "Basit Doğrusal İş Son Ek Dizi Yapısı" bölümünde açıklanan çarpık algoritmayı kullanır. Bu makalenin içerdiği C ++ uygulaması zaten biraz "golf" hissediyor, ancak daha kısa yapmak için hala yeterince yer var. Örneğin, O(n)gerekliliği ihlal etmeden, kağıtta olduğu gibi kısa devre yapmak yerine, bir uzunluk uzunluğuna ulaşana kadar tekrarlayabiliriz .

LCP bölümü için, Kusai ve ark.nın "Son Dizilerde ve Uygulamalarındaki Son Zamanlı En Uzun-Ortak Önek Hesaplamasını" izledim.

En 1 0uzun kullanılan alt dize boşsa program çıkar.

C ++ uygulamasını biraz daha yakından izleyen programın daha eski bir sürümünü, karşılaştırma için bazı daha yavaş yaklaşımları ve basit bir test senaryosu oluşturucusunu içeren bazı geliştirme kodları:

from random import *

def brute(a,b):
    L=R=m=0

    for i in range(len(a)):
        for j in range(i+m+1,len(a)+1):
            if a[i:j] in b:
                m=j-i
                L,R=i,j

    return L+1,R

def suffix_array_slow(s):
    S=[]
    for i in range(len(s)):
        S+=[(s[i:],i)]
    S.sort()
    return [x[1] for x in S]

def slow1(a,b):
    # slow suffix array, slow lcp

    s=a+'!'+b
    S=suffix_array_slow(s)

    L=R=m=0

    for i in range(1,len(S)):
        x=S[i-1]
        y=S[i]
        p=s[x:]+'#'
        q=s[y:]+'$'
        h=0
        while p[h]==q[h]:
            h+=1
        if h>m and len(a)==sorted([x,y,len(a)])[1]:
            m=h
            L=min(x,y)
            R=L+h

    return L+1,R

def verify(a,b,L,R):
    if L<1 or R>len(a) or a[L-1:R] not in b:
        return 0
    LL,RR=brute(a,b)
    return R-L==RR-LL

def rand_string():
    if randint(0,1):
        n=randint(0,8)
    else:
        n=randint(0,24)
    a='zyxwvutsrq'[:randint(1,10)]
    s=''
    for _ in range(n):
        s+=choice(a)
    return s

def stress_test(f):
    numtrials=2000
    for trial in range(numtrials):
        a=rand_string()
        b=rand_string()
        L,R=f(a,b)
        if not verify(a,b,L,R):
            LL,RR=brute(a,b)
            print 'failed on',(a,b)
            print 'expected:',LL,RR
            print 'actual:',L,R
            return
    print 'ok'

def slow2(a,b):
    # slow suffix array, linear lcp

    s=a+'!'+b+'#'
    S=suffix_array_slow(s)

    I=S*1
    for i in range(len(S)):
        I[S[i]]=i

    L=R=m=h=0

    for i in range(len(S)):
        if I[i]:
            j=S[I[i]-1]
            while s[i+h]==s[j+h]:
                h+=1
            if h>m and len(a)==sorted([i,j,len(a)])[1]:
                m=h
                L=min(i,j)
                R=L+h
            h-=h>0

    return L+1,R

def suffix_array(s,K):
    # skew algorithm

    n=len(s)
    s+=[0]*3
    n0=(n+2)/3
    n1=(n+1)/3
    n2=n/3
    n02=n0+n2
    adj=n0-n1

    def radix_pass(a,o,n=n02):
        c=[0]*(K+3)
        for x in a[:n]:
            c[s[x+o]+1]+=1
        for i in range(K+3):
            c[i]+=c[i-1]
        for x in a[:n]:
            j=s[x+o]
            a[c[j]]=x
            c[j]+=1

    A=[x for x in range(n+adj) if x%3]+[0]*3

    radix_pass(A,2)
    radix_pass(A,1)
    radix_pass(A,0)

    B=[0]*n02
    t=m=0

    for x in A[:n02]:
        u=s[x:x+3]
        m+=t<u
        t=u
        B[x/3+x%3/2*n0]=m

    A[:n02]=1/n02*[0]or suffix_array(B,m)
    I=A*1
    for i in range(n02):
        I[A[i]]=i+1

    B=[3*x for x in A if x<n0]
    radix_pass(B,0,n0)

    R=[]

    p=0
    t=adj
    while t<n02:
        x=A[t]
        b=x>=n0
        i=(x-b*n0)*3-~b
        j=B[p]
        if p==n0 or ((s[i:i+2],I[A[t]-n0+1])<(s[j:j+2],I[j/3+n0]) if b else (s[i],I[A[t]+n0])<(s[j],I[j/3])):R+=i,;t+=1
        else:R+=j,;p+=1

    return R+B[p:n0]

def solve(a,b):
    # linear

    s=a+'!'+b+'#'
    S=suffix_array(map(ord,s),128)

    I=S*1
    for i in range(len(S)):
        I[S[i]]=i

    L=R=m=h=0

    for i in range(len(S)):
        if I[i]:
            j=S[I[i]-1]
            while s[i+h]==s[j+h]:
                h+=1
            if h>m and len(a)==sorted([i,j,len(a)])[1]:
                m=h
                L=min(i,j)
                R=L+h
            h-=h>0

    return L+1,R

stress_test(solve)

1
Eğer hatalıysam düzelt, ama bu aslında 739 byte değil mi? Mothereff.in/byte-counter içine kopyaladım ve 6-9. Satırlardan 2 boşluk sildim, ama doğru olup olmadığından emin değilim.
Patrick Roberts

2
@PatrickRoberts Bunlar sekmeler.
Mitch Schwartz

2
Güzel cevap! 2016'dan itibaren GSACA'ya yeni bir lineer zaman SACA'ya bakmak isteyebilirsiniz. Referans uygulaması 246 satırdır (yorumsuz 170) ve çok golf oynamaktadır. Onu github'da bulacaksın.
Christoph

1
@MitchSchwartz Şu anda noPMO'da kalmaya çalışıyorum, bu yüzden şu anda duyguları çok güçlü hissedemiyorum (muhtemelen dengesiz beyin kimyasalları nedeniyle). Kodu hızlıca okurken, sözdizimi golf motorum bunu fark etti ve herhangi bir özel duygu hissettiğimi hatırlamıyorum. Aynı şeyi mi düşündünüz ya da neden soru? :) Şimdi merak ediyorum.
Yytsi

1
@TuukkaX Beklemediğim ilginç bir cevap. Bunu özel bir şekilde söylemem gerekip gerekmediğinden emin değilim, ancak orijinal yorumunuzun gerçekten doğru olmadığı gerçeği, neden sormaya karar verdiğimde bir rol oynadı. :)
Mitch Schwartz
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.