Loading [MathJax]/jax/output/CommonHTML/jax.js
본문 바로가기
알고리즘/필기

Z

by 유시은 2020. 8. 15.

길이 n의 문자열 S[0:n]에 대하여 ArrayZ[0:n]O(n)에 구한다.

 

i Z a n a n a b
0 6 a n a n a b
1 0   n a n a b
2 3     a n a b
3 0       n a b
4 1         a b
5 0           b

Z[i]S[0:]S[i:]의 최대 공통 접두사의 길이이다.

 


코드는 다음과 같이 작성하였다.

 

def getZ(s):
    l,r=0,0
    ret=[0]*len(s)
    ret[0]=len(s)

    for i in range(1,len(s)):
        if i>r:
            l=r=i
            while r<len(s) and s[r]==s[r-l]:
                r+=1
            ret[i]=r-l
            r-=1
        else:
            if ret[i-l]<=r-i:
                ret[i]=ret[i-l]
            else:
                l=i
                while r<len(s) and s[r]==s[r-l]:
                    r+=1
                ret[i]=r-l
                r-=1

    return ret
    

 

구간 [l:r]S[l:r]S[0:]의 접두사가 일치하면서 r이 최대인 구간으로 정의한다.

 

그러면 Z[i]는 각 i에서의 구간의 길이 rl 와 같다.

 

 

Z[0]은 S의 길이와 같음이 자명하다. lr0으로 초기화 한다.

 

이제 i=1부터 i=|s|1까지 다음과 같은 과정을 반복한다.

 

 

  • Case1: i>r일 때

아무 정보가 없는 곳에 도달하였다.

 

lr을 현 위치로 이동하고, S[r]S[rl]이 달라질 때 (구간 [l:r]의 정의를 위배) 까지 r을 오른쪽으로 이동한다.

 

이렇게 구한 lr의 차로 Z[i]의 값을 구할 수 있다.

 

 

  • Case2: ir일 때

현재 가지고 있는 정보가 완전할 때와 그렇지 않을 때로 나눌 수 있다.

 

 

Z[il]ri에서 가지고 있는 정보는 완전하다.

 

S[l:r]S[0:rl]이 같으므로 Z[i]Z[il]은 같다.

 

 

Z[il]>ri에서 가지고 있는 정보는 제한적이다. S[i:]S[0:]의 접두사가 ri글자 이상 일치할 수 있다는 뜻이다.

 

li로 이동하고 Case1과 같이 r을 이동시키면 Z[i]의 값을 구할 수 있다.

 


 

처음 표에서 쓴 문자열 ananab으로 자세히 알아보자.

 

 

Z[0]

문자열의 길이와 같으므로 6이다.

 

lr을 0으로 초기화 한다.

 

 

Z[1] (i=1,l=0,r=0)

ir보다 커 Case1에 해당한다.

 

S a n a n a b
S[i:]   n a n a b
l,r l, r = 1          

lr1(=i) 로 초기화 한 다음, S[r]S[rl]이 달라질 때 까지 r을 오른쪽으로 이동시킨다.

 

하지만 처음부터 S[1]S[11]이 각각 n과 a로 다르므로 l=1, r=1로 끝난다.

 

그 결과 Z[1]=0이다.

 

이렇게 바로 끝날 때 r이 l 뒤로 가는데, 그 다음 호출에선 다시 Case 1로 돌아와 l=r=i를 실행하므로 문제 없는 것 같다.

 

 

Z[2] (i=2,l=1,r=0)

ir보다 커 Case1에 해당한다.

 

S a n a n a b
S[i:]     a n a b
l,r   l = 2     r = 5  

lr2(=i) 로 초기화 한 다음, S[r]S[rl]이 달라질 때 까지 r을 오른쪽으로 이동시킨다.

 

r=5에서 S[5]S[52]이 각각 b와 n으로 다르므로 l=2, r=5로 끝난다.

 

그 결과 Z[2]=3이다.

 

 

Z[3] (i=3,l=2,r=4)

ir이하이고, Z[32]43이므로 Case2중 첫 째 경우에 해당한다.

 

S a n a n a b
S[i:]       n a b
l,r            

S[l:r]S[0:rl]이 일치함을 확신할 수 있고, 실제로 ana (anana)로 같다.

 

따라서 Z[3]Z[1]도 같다고 확신할 수 있다.

 

Z[3]=Z[1]=0 이다.

 

 

Z[4] (i=4,l=2,r=4)

ir이하이고, Z[42]44가 성립하지 않아 Case2중 둘 째 경우에 해당한다.

 

S a n a n a b
S[i:]         a b
l,r       l = 4 r = 5  

li(=4)로 이동하고, S[r]S[rl]이 달라질 때 까지 r을 오른쪽으로 이동시킨다.

 

r=5에서 S[5], S[54]이 각각 b, n으로 달라지므로 l=4, r=5로 끝난다.

 

따라서 Z[4]=1이다.

 

 

i=5 에서도 Case1에서의 논리로 값을 구할 수 있고, 최종적으로 Z=[6,0,3,0,1,0] 을 얻을 수 있다.

 


공부한 곳

https://anz1217.tistory.com/66

https://codeforces.com/blog/entry/3107

'알고리즘 > 필기' 카테고리의 다른 글

라빈-카프와 부분문자열 쿼리  (0) 2022.07.22
연속한 수의 XOR sum  (0) 2020.12.25
치킨 쿠폰  (0) 2020.11.25
유니온 파인드  (0) 2020.09.11

댓글