접미사 배열의 몇 가지 기본 개념은 Baidu를 부탁드립니다. 간단히 말해서, 접미사 어레이는 문자열의 모든 접미사 크기의 모음입니다. 그런 다음 접미사 배열의 속성을 기반으로 다양한 요구를 달성 할 수 있습니다.
공개 클래스 mysuffixArrayTest {public char [] 접미사; // 원본 문자열 public int n; // String 길이 public int [] rank; // 모든 접미사 int [] sa; // 접미사 [SA [SA [len]]… Public int [] height; //는 접미사 [SA [i]] 및 접미사 [SA [i -1]], 즉 두 개의 인접한 접미사의 가장 긴 공개 접두사, 공개 int [] h; //는 높이와 같다 [I]], 접미사 [i]의 가장 긴 대중 접두사와 접근 한 접근법의 접근은 [] [] ws; // count auxting sortay intix intix of public intix의 가장 긴 공개 접두사입니다. y; // 두 번째 키워드 순위 배열 공개 int [] x; // rank auxiliary array}다음과 같은 설명은 "Aabaaaab"을 예로 들어 봅니다. 먼저 결과를 보여 드리겠습니다. 이해 및 분석을 위해이 결과를 참조하십시오 (이 결과에 대한 다른 사람의 사진을 복사했습니다. 내 배열이 첨자 0으로 시작하기 때문에 기본적으로 1을 위시하십시오).
접미사 : 원래 문자열 배열은 원래 문자열이 "aabaaaab"이라고 가정하고,이 배열의 해당 값은 { 'a', 'a', 'b', 'a', 'a', 'a', 'b'}이어야합니다.
N : 여기서 스트링 길이는 8입니다
순위 : 접미사 배열의 순위 배열은 I-TH 접미사에 해당하는 순위와 같습니다. 예를 들어, 순위 [0]는 접미사 "Aabaaaab"순위 [1]의 순위를 말합니다.
SA : 이것은 순위 배열에 반전되는 배열입니다. X- 노드가 접미사를 저장합니까? 또는 SA [0]이 1 위 접미어 배열, 즉 3. 즉, 배열의 해당 순위 [3]를 지칭한다는 것을 설명하기 위해 예를 들어 보려면 예를 들어, 배열 [i] = i 공식을 이해하십시오. SA와 계급의 관계를 이해하면 이해해야합니다.
높이 : 높이 [i]는 SA [i] 접미사 어레이의 가장 큰 일반적인 접두사의 길이이며 SA [I-1] 접미사 어레이 높이 [1]은 "AAAB"와 "AAAAB"의 가장 큰 일반적인 접두사 인 두 번째 및 첫 번째 가장 큰 일반적인 접두사 SA [1] 및 SA [0]을 말합니다.
H : H [i]는 I-th 접미사를 말하며 이전 h [0]의 가장 큰 공개 접두사는 첫 번째 접미사, 즉 "aabaaaab"과 이전의 가장 큰 공개 접두사, 즉 "AAB", 즉 높이 [0] = 높이 [3] = 3을 말합니다. 이것은 이해하기 어렵습니다. 당신은 당시에는 이해할 수없고 계속 읽을 수 있습니다.
WS : 할 말이 없음, 보조 배열 분류를 계산하십시오
Y : 두 번째 키워드 정렬은 두 번째 키워드가 두 번째 키워드에 해당하는 SA 배열입니다.
X : 순위 배열의 백업으로 이해할 수 있습니다. 처음에는 순위 배열 백업을 사용한 다음 각 루프 다음에 순위 배열을 기록합니다.
먼저 SA 배열 코드를 살펴 보겠습니다. 코드의 기능을 하나씩 설명하고 총 코드를 다음에 첨부합니다.
rank = new int [n]; sa = new int [n]; ws = 새로운 int [255]; y = new int [n]; x = 새로운 int [n]; // int 값을 (int i = 0; i <n; i ++)의 순위 배열로 변환하기 위해 원래 문자열을 루프합니다. {rank [i] = (int) 접미사 [i]; }위의 코드의 기능은 배열을 초기화하고 첫 번째 계산 및 정렬을 수행하는 것입니다. 첫 번째 루프는 초기 값을 순위 배열에 할당하는 것입니다. 실행 후, 순위 배열의 해당 값은 {97, 97, 98, 97, 97, 97, 97, 98}입니다. 순위 배열의 초기 값은 문자에 해당하는 ASCII 코드임을 알 수 있습니다.
다음 세 사이클은 첫 번째 계수 분류입니다. 정렬 계산을 이해하지 못하면 Baidu를 부탁드립니다. 이 세 사이클의 과정에 대해 이야기하겠습니다.
for (int i = 0; i <n; i ++) {ws [rank [i]] ++; x [i] = 순위 [i]; } for (int i = 1; i <ws.length; i ++) {ws [i]+= ws [i -1]; }이 두 루프가하는 일은 모든 발생 값을 계산하고 순위 배열을 X 배열로 백업하는 것입니다. 첫 번째 루프가 실행 된 후 WS [97] = 6, WS [98] = 2, 두 번째 루프가 실행되면 WS [97] = 6, WS [98] = 8
for (int i = n-1; i> = 0; i-) {sa [-WS [rank [i]] = i; }위 단락은 SA 배열을 찾기 위해 계산 및 정렬을위한 특정 코드입니다. 모든 사람이 처음 읽을 때 오해했을 것입니다. 그들은 왜 SA를 찾았습니까? 나는 또한 처음으로 혼란 스러웠지만 인내심을 가지고이 코드를주의 깊게 이해하십시오. 예를 들어 접미사 "B"에 대해 위에서 언급 한 공식을 여전히 기억하십니까? 분명히 SA [98]은 존재하지 않지만 WS 배열에 98이 나타나는 횟수를 기록 했으므로 WS [98]은 "B"의 해당 순위가되어야합니다. SA가되기 위해 1을 빼는 것을 잊지 마십시오 [-ws [rank [i]] = i. 왜 뒤로 이동 해야하는지에 대해서는 여기에서 신중하게 이해해야합니다. 그렇지 않으면 두 번째 키워드에 따라 정렬하는 방식으로 완전히 눈을 멀게 할 것입니다. 동일한 두 개의 순위 값이 있으면 어떻게 정렬합니까? SA 어레이 앞에 먼저 나타나야합니다. 이 루프와 WS 배열 값의 변경 사항에 대해 생각하면 For 루프의 순서가 실제로 순위 값이 동일 할 때 배열 순서를 나타냅니다. 뒤쪽에서 앞쪽으로 이동하면 순위 값이 동일 할 때 접미사 순위가 낮다는 것을 의미합니다.
위의 것은 첫 번째 계산 분류 일 뿐이며, 각 접미사 배열의 첫 글자를 비교하여 SA를 찾는 것과 같습니다. 해당 결과는 아래 그림과 같습니다.
// 루프 조합 정렬 (int j = 1, p = 0; for (int i = n -j; i <n; i ++) {y [p ++] = i; } // 첫 번째 키워드 SA에 따라 두 번째 키워드 (int i = 0; i <n; i ++) {if (sa [i]> = j) {y [p ++] = sa [i] -j; }} // 두 키워드를 정렬합니다 (int i = 0; i <ws.length; i ++) {ws [i] = 0; } for (int i : x) {ws [i] ++; } for (int i : x) {ws [i] ++; } for (int i = 1; i <ws.length; i ++) {ws [i]+= ws [i -1]; } for (int i = n-1; i> = 0; i-) {sa [-ws [x [y [i]] = y [i]; y [i] = 0; } // sa int xb [] = new int [n]; // x 배열 백업 (int i = 0; i <n; i ++) {xb [i] = x [i]; } int 번호 = 1; X [SA [0]] = 1; for (int i = 1; i <n; i ++) {if (xb [sa [i]]! = xb [sa [i -1]]) {x [sa [i]] = ++ 숫자; } else if (sa [i] + j> = n && sa [i -1] + j> = n) {x [sa [i]] = 숫자; } else if (sa [i] + j <n && sa [i -1] + j> = n) {x [sa [i]] = ++ 숫자; } else if (xb [sa [i] + j]! = xb [sa [i -1] + j]) {x [sa [i]] = ++ 숫자; } else {x [sa [i]] = 숫자; } if (number> = n) break; }}이것은 SA 배열을 찾을 때 이해하기 가장 어려운 코드입니다. 우선 곱셈 알고리즘의 아이디어를 이해해야합니다. 첫 번째 계산 순서 후에, 우리는 이미 모든 접미사 배열의 첫 번째 초기 문자의 정렬을 이미 알고 있습니까? 우리는 첫 번째 초기 문자의 분류가 그의 두 번째 문자의 순서와 동일하다는 것을 알고 있기 때문에 (정렬과 순서의 차이점에 유의하십시오. 정렬은 그가 어떤 것을 고정했는지 알고 있다는 것입니다. 순서는 그가 나타나는 순서 만 알고 있지만 그가 특정 순서를 알지 못한다는 것입니다). 물론 이것은 원래 문자열에서 왔고 각 접미사에 대해 이전 접미사의 접미사로도 사용할 수 있기 때문입니다. 예를 들어, "Baaaab"의 경우 첫 번째 문자의 순서는 "abaaaab"의 두 번째 키워드 순서에 해당합니다. 첫 번째 키워드의 순서와 두 번째 키워드의 순서를 사용하면 두 가지 키워드의 종류를 찾을 수 있습니다. 조합 정렬의 결과에 따르면 이전 아이디어를 사용할 수 있습니다. "Baaaab"의 첫 번째 조합 후, 우리는 첫 두 글자 "ba"의 순서를 분류하므로 "Aabaaaab"의 두 번째 키워드의 순서를 사용할 수도 있습니다. 전체 종류의 논리는 아래에 참조되어 있습니다
그런 다음 세그먼트에서 코드를 분석합니다
for (int i = n -j; i <n; i ++) {y [p ++] = i; } // 첫 번째 키워드 SA에 따라 두 번째 키워드를 선택하십시오 (int i = 0; i <n; i ++) {if (sa [i]> = j) {y [p ++] = sa [i] -j; }}위의 코드는 SA, 즉 두 번째 키워드의 Y 배열을 찾는 것입니다. P의 초기 값은 0이고 첫 번째 루프는 배열의 전면에 채워야하는 접미사를 순위하는 것입니다.
이전 로직 다이어그램과 함께 두 번째 루프의 논리를 이해해야합니다. 첫 번째 키워드 SA의 분류 결과를 통과합니다. if (sa [i]> = j)는 접미사가 다른 접미사의 두 번째 키워드로 사용할 수 있는지 여부를 결정합니다. SA [i] = 0이 접미사 배열 "aabaaaab"을 나타낼 때 첫 번째 루프 j = 1을 예로 들어 보겠습니다. 분명히 다른 접미사의 두 번째 키워드로 사용할 수 없습니다. 다른 접미사로 사용할 수있는 두 번째 키워드의 경우 SA의 순서가 해당 두 번째 키워드입니다. SA [i] -J는 그의 두 번째 키워드로서 그의 접미사를 찾아서 y 어레이에 넣고 p ++를 넣습니다. 여기서 천천히 이해해야합니다.
// (int i = 0; i <ws.length; i ++) {ws [i] = 0; } for (int i : x) {ws [i] ++; } for (int i = 1; i <ws.length; i ++) {ws [i]+= ws [i -1]; } for (int i = n-1; i> = 0; i-) {sa [-WS [x [y [i]]] = y [i]; y [i] = 0; }위는 첫 번째 키워드 정렬 SA와 두 번째 키워드 정렬을 기반으로 조합 정렬을 찾는 것입니다. 이 코드는 매우 모호합니다. 우리는 먼저 코드를 이해할 수 없지만 아이디어를 이해할 수 있습니다. 두 키워드의 정렬의 경우 실제 규칙은 두 숫자의 정렬과 유사합니다. 예를 들어, 11 및 12 크기 비교, 10 비트는 첫 번째 키워드이며 단일 비트는 두 번째 키워드입니다. 10 비트를 비교 한 후 11 = 12를 찾은 다음 단일 비트를 비교하면 11 <12라는 것을 알고 있습니다. 10 비트가 동일하면 단일 비트의 순서는 크기 순서입니다. 루프에 대한 카운트 분류 순서가 실제로 순위 값이 동일 할 때 배열 순서를 나타냅니다. 그렇다면 두 키워드가 하나의 카운트 정렬로 병합 된 후에 순서를 어떻게 찾을 수 있습니까? 내 이해를 말해 드리겠습니다. 하나의 카운트 정렬에는 실제로 두 종류의 종류가 포함되어 있고, 하나는 일종의 수치 값이고, 다른 하나는 일종의 발생 순서입니다. 규칙은 11과 12의 비교의 이전 예와 같습니다. 숫자 값의 종류는 10 비트이며, 발생 순서는 하나의 비트입니다. 이 시점에서 우리는 아이디어가 있습니다. 값의 정렬은 첫 번째 키워드로 정렬되며 발생 정렬은 두 번째 키워드로 정렬되므로 두 키워드가 결합 된 후에 정렬을 찾기 위해 한 번에 계산하고 정렬 할 수 있습니다. 위의 코드는이 아이디어의 구현입니다. X 배열은 첫 번째 키워드의 순위 배열이며 계산합니다.
for (int i = n-1; i> = 0; i-) {sa [-WS [x [y [i]]] = y [i]; y [i] = 0; }이 루프는 위의 모든 아이디어를 구현하는 것입니다. 우리는 뒷면에서 두 번째 키워드 배열 y를 가로 지릅니다. Y [i]의 경우 첫 번째 키워드의 카운트 순위를 계산합니다. 이 카운트 순위는 y [i]의 순위이며 최종 카운트는 1 씩 줄어 듭니다. 병합 키워드 정렬이 성공적으로 발견되었습니다.
위의 모든 코드를 이해한다면 분명히 놀랄 것입니다. 나는이 코드에 대해 계속해서 생각했을 때 흥분했고, 나는 단순히 확신했다. 이것은 알고리즘의 매력입니다.
SA 배열을 사용하면 순위 배열을 찾을 수 있습니다. 이것은 어렵지 않으므로 설명하지 않을 것입니다. SA 찾기를위한 모든 코드는 아래에 첨부되어 있습니다.
public static void main (String [] args) {String str = "aabaaaab"; MySuffixArrayTest ArrayTest = New MySuffixArrayTest (str.toString ()); ArrayTest.initsa (); // sa array 찾기} public void initsa () {rank = new int [n]; sa = new int [n]; ws = 새로운 int [255]; y = new int [n]; x = 새로운 int [n]; // int 값을 (int i = 0; i <n; i ++)의 순위 배열로 변환하기 위해 원래 문자열을 루프합니다. {rank [i] = (int) 접미사 [i]; } // (int i = 0; i <n; i ++) {ws [rank [i]] ++; x [i] = 순위 [i]; } for (int i = 1; i <ws.length; i ++) {ws [i]+= ws [i -1]; } for (int i = n-1; i> = 0; i-) {sa [-ws [rank [i]] = i; } // 루프 조합 정렬 (int j = 1, P = 0; for (int i = n -j; i <n; i ++) {y [p ++] = i; } // 첫 번째 키워드 SA에 따라 두 번째 키워드 (int i = 0; i <n; i ++) {if (sa [i]> = j) {y [p ++] = sa [i] -j; }} // 두 키워드를 정렬합니다 (int i = 0; i <ws.length; i ++) {ws [i] = 0; } for (int i : x) {ws [i] ++; } for (int i = 1; i <ws.length; i ++) {ws [i]+= ws [i -1]; } for (int i = n-1; i> = 0; i-) {sa [-ws [x [y [i]] = y [i]; y [i] = 0; } // sa int xb [] = new int [n]; // x 배열 백업 (int i = 0; i <n; i ++) {xb [i] = x [i]; } int 번호 = 1; X [SA [0]] = 1; for (int i = 1; i <n; i ++) {if (xb [sa [i]]! = xb [sa [i -1]]) {x [sa [i]] = ++ 숫자; } else if (sa [i] + j> = n && sa [i -1] + j> = n) {x [sa [i]] = 숫자; } else if (sa [i] + j <n && sa [i -1] + j> = n) {x [sa [i]] = ++ 숫자; } else if (xb [sa [i] + j]! = xb [sa [i -1] + j]) {x [sa [i]] = ++ 숫자; } else {x [sa [i]] = 숫자; } if (number> = n) break; }}}}요약
위는 귀하에게 소개 된 Java 접미사 배열의 SAC 어레이에 대한 예제 코드입니다. 나는 그것이 당신에게 도움이되기를 바랍니다. 궁금한 점이 있으면 메시지를 남겨 주시면 편집자가 제 시간에 답장을 드리겠습니다. Wulin.com 웹 사이트를 지원해 주셔서 대단히 감사합니다!