O (n)에서 접미사 배열을 사용하여 문자열의 최소 사전 회전


9

ACM 2003에서 문제를 인용하겠습니다.

길이 n의 문자열 (1 <= n <= 100000)을 고려하십시오. 최소 사전 사전 회전을 결정하십시오. 예를 들어 문자열 "alabala"의 회전은 다음과 같습니다.

앨라 배라

음순

아 발랄

발라 알라

알라 랩

라라 바

알라 발

그중에서 가장 작은 것은 "알라 발"입니다.

해결책에 관해서는- 접미사 배열 을 구성해야한다는 것을 알고 있으며 O (n)에서 그렇게 할 수 있다고 가정 해 봅시다. 내 질문은 여전히 ​​O (n)에서 가장 작은 회전을 어떻게 찾을 수 있습니까?입니다. (n = 문자열 길이)

나는이 문제에 매우 관심이 있고 여전히 해결책을 얻지 못합니다. 구체적인 구현이 아니라 개념과 문제 해결 방법에 더 관심이 있습니다.

참고 : 최소 회전은 영어 사전에서와 동일한 순서를 의미합니다. d는 w보다 앞에 있기 때문에 "dwor"는 "word"앞에 있습니다.

편집 : 접미사 배열 구성에는 O (N)이 필요합니다

마지막 편집 : 해결책을 찾은 것 같습니다 !!! 두 문자열을 병합하면 어떻게됩니까? 따라서 문자열이 "alabala"이면 새 문자열은 "alabalaalabala"이고 이제이 접미사 배열 (O (2n) = O (n))을 구성하고 첫 번째 접미사를 얻었습니까? 나는 이것이 옳을 것 같아요. 어떻게 생각해? 감사합니다!


"최소"를 어떻게 정의합니까? 사용 된 측정 항목은 무엇입니까 (어쩌면 분명하지만 전문가는 아닙니다)?
— Giorgio

메모 주셔서 감사합니다! 나는 회전 순서 사전의 결과가 아니라 최소 회전 (최소 오프셋)이어야한다고 생각했습니다.
— Giorgio

여전히 뭔가 빠졌습니다. 접미사 배열의 구성과 정렬이 복잡성에 포함되어 있습니까? 배열을 구성 하고 정렬하는 데 O (n) 이상이 필요하다고 생각 합니다.
— Giorgio

원래 문자열을 두 번 반복한다는 아이디어가 훌륭하다고 생각합니다! 그런 다음 O (2n) = O (n)에 접미사 배열을 만들 수 있습니다. 그러나 최소값을 찾기 위해 정렬 할 필요가 없습니까? 이것은 O (n) 이상이 필요합니다.
— Giorgio

@Giorgio 글쎄, 접미사 배열 자체는 이미 정렬 된 접미사를 보유합니다 . 그리고 또 다른 메모, 아마도 약간 주제가 아닐 수도 있습니다-정렬 된 객체에 대한 몇 가지 가정을 통해 o (n)에서도 정렬을 수행 할 수 있다는 것을 잊지 마십시오 (예 : 기수 정렬을 확인하십시오)
— Tomy

답변:


5

길이가 N 인 문자열의 모든 회전을 구성하는 간단한 방법은 문자열을 자체와 연결하는 것입니다.

그런 다음이 2N 길이 문자열의 모든 N 길이 하위 문자열은 원래 문자열의 회전입니다.

그런 다음 O (N) 트리 구성을 사용하여 "사 전적으로 최소"하위 문자열을 찾습니다.


0

접미사 배열에 포함 된 정보가 O (n)에 도달하는 데 충분하지 않지만 O (n log n)에 도움이 될 수 있다고 확신합니다. 이 접미사 계열을 고려하십시오.

a
aba
abacaba
abacabadabacaba
abacabadabacabaeabacabadabacaba
...

이전 접미사 (예 : aba)를 가져 와서 아직 사용되지 않은 다음 문자를 추가 한 다음 이전 접미사를 다시 추가하여 다음 접미사를 구성합니다 (aba-> aba c aba).

이제 다음 문자열을 고려하십시오 (공백이 강조되도록 추가되었지만 문자열의 일부는 아님).

ad abacaba
bd abacaba
cd abacaba

이 세 문자열의 경우 접미사 배열의 시작은 다음과 같습니다.

a
aba
abacaba
(other suffixes)

익숙해 보이나요? 이 문자열은 물론이 접미사 배열을 만들도록 조정되었습니다. 이제 시작 문자 (a, b 또는 c)에 따라 '올바른'지수 (문제의 해결책)는 위 목록의 첫 번째, 두 번째 또는 세 번째 접미사입니다.

첫 글자 선택은 접미사 배열에 거의 영향을 미치지 않습니다. 특히 접미사 배열의 처음 세 접미사 순서에는 영향을 미치지 않습니다. 이것은 접미사 배열이 매우 유사하지만 '올바른'색인이 매우 다른 log n 문자열을 가지고 있음을 의미합니다.

나는 확실한 증거는 없지만, 어휘 순서에 대해 배열의 처음 3 개의 인덱스에 해당하는 회전을 비교하는 것 외에는 선택의 여지가 없다는 것을 강력하게 제안합니다. 이는 적어도 O (n log n) 시간 (대체 첫 문자의 수-우리의 경우 3-은 log n이므로 두 문자열을 비교하는 데 O (n) 시간이 걸립니다).

이것은 O (n) 알고리즘의 가능성을 배제하지 않습니다. 서 픽스 배열이이 러닝 타임을 달성하는 데 도움이된다는 의문이 있습니다.


0

가장 작은 회전은 접미사 배열의 일부 접미사로 시작하는 회전입니다. 접미어는 사전 식으로 정렬됩니다. 이것은 당신에게 큰 도약을 제공합니다 :

  • 접미사 k 로 시작하는 회전이 접미사 k +1로 시작하는 회전보다 작은 k 를 얻었 으면 완료된 것입니다 (처음부터 시작).
  • 당신은 "접미사로 시작하는 회전을 비교 한 작업을 수행 할 수 있습니다 K 접미사로 선발진보다 작은 K 서로 문자로 한 문자를 비교, 선택적 접미사의 길이를 비교하여 O (1)에서 일을."

편집 : "다른 문자가있는 한 문자"는 항상 같지는 않지만 하나 이상의 문자 일 수도 있지만 전체적으로 전체 검색 프로세스를 통해 n 문자 이상을 검사하지 않으므로 O (n)입니다.

짧은 증거 : 접미사 k +1이 접미사 k 보다 긴 경우에만 문자를 검사 하고 접미사 k +1이 접미사 k 보다 짧은 경우 해결책을 중지하고 발견합니다 (그러면 접미사 k 가 원하는 것을 알 수 있습니다). 따라서 접미사 순서가 길어질 때만 문자를 검사합니다. 초과 문자 만 검사하므로 n 개 이상의 문자를 검사 할 수 없습니다.

EDIT2 :이 알고리즘은 "접미사 배열에 두 개의 인접 접미사가 있고 이전이 다음보다 짧으면 이전이 후속의 접두사"라는 사실에 의존합니다. 이것이 사실이 아니라면 죄송합니다.

EDIT3 : 아니요, 보류하지 않습니다. "abaaa"에는 접미사 테이블 "a", "aa", "aaa", "abaaa", "baaa"가 있습니다. 그러나 아마도 이러한 생각은 궁극적으로 해결책으로 이어질 수 있습니다. 더 자세한 것은 더 정교해야합니다. 주된 질문은 적은 문자를 검사하여 위에서 언급 한 비교를 수행 할 수 있는지 여부입니다. 따라서 O (n)은 완전히 가능합니다. 나는 지금 어떻게 말할 수 없다.


0

문제:

사 전적으로 최소 원형 하위 문자열은 그러한 모든 회전 중에서 가장 낮은 사전 순을 갖는 스트링의 회전을 찾는 문제이다. 예를 들어, "bbaaccaadd"의 사 전적으로 최소 회전은 "aaccaaddbb"입니다.

해결책:

AO (n) 시간 알고리즘은 Jean Pierre Duval (1983)에 의해 제안되었습니다.

두 지표 감안 i하고 j, 듀발의 알고리즘은 길이의 문자열 세그먼트 비교 j - i에서 시작 i하고 j(불리는 "결투" ). 경우 index + j - i문자열의 길이보다 큰 경우, 세그먼트가 주위에 배치하여 형성된다.

예를 들어, s = "baabbaba", i = 5 및 j = 7을 고려하십시오. j-i = 2이므로 i = 5에서 시작하는 첫 번째 세그먼트는 "ab"입니다. j = 7에서 시작하는 두 번째 세그먼트는 줄 바꿈으로 구성되며 "ab"입니다. 위의 예에서와 같이 문자열이 사전 식으로 동일한 경우, i에서 시작하는 문자열을 i = 5로 선택합니다.

위 과정은 한 명의 당첨자가 될 때까지 반복됩니다. 입력 문자열의 길이가 홀수 인 경우 첫 번째 반복에서 비교하지 않고 마지막 문자가 승리합니다.

시간 복잡성 :

첫 번째 반복은 길이 1 (n / 2 비교) 각각의 n 개 문자열을 비교하고, 두 번째 반복은 i 번째 반복이 2 개의 문자열을 비교할 때까지 길이 2의 n / 2 문자열 (n / 2 비교) 등을 비교할 수 있습니다. 길이 n / 2 (n / 2 비교). 승자의 수는 매번 반으로 줄어들 기 때문에 재귀 트리의 높이는 log (n)이므로 O (n log (n)) 알고리즘을 제공합니다. 작은 n의 경우 이것은 대략 O (n)입니다.

첫 번째 반복에서는 n / 2 수상자, 두 번째 반복 n / 4 수상자 등을 저장해야하므로 공간 복잡도도 O (n)입니다. (Wikipedia는이 알고리즘이 일정한 공간을 사용한다고 주장합니다. 어떻게 이해하지 못합니다).

다음은 스칼라 구현입니다. 좋아하는 프로그래밍 언어로 자유롭게 변환하십시오.

def lexicographicallyMinRotation(s: String): String = {
 @tailrec
 def duel(winners: Seq[Int]): String = {
   if (winners.size == 1) s"${s.slice(winners.head, s.length)}${s.take(winners.head)}"
   else {
     val newWinners: Seq[Int] = winners
       .sliding(2, 2)
       .map {
         case Seq(x, y) =>
           val range = y - x
           Seq(x, y)
             .map { i =>
               val segment = if (s.isDefinedAt(i + range - 1)) s.slice(i, i + range)
               else s"${s.slice(i, s.length)}${s.take(s.length - i)}"
               (i, segment)
             }
             .reduce((a, b) => if (a._2 <= b._2) a else b)
             ._1
         case xs => xs.head
       }
       .toSeq
     duel(newWinners)
   }
 }

 duel(s.indices)
}

-1

O (N²)보다 더 좋은 것은 없습니다.

N 개의 정수 목록이 있으면 가장 작은 O (N) 비교를 선택할 수 있습니다.

여기에 N 크기의 N 문자열 목록이 있습니다 (비용이 들지 않으면 문자열은 시작 색인에 의해 완전히 결정됩니다). O (N) 비교에서 가장 작은 것을 선택할 수 있습니다. 그러나 각 비교는 O (N) 기본 연산입니다. 복잡성은 O (N²)입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.