2013-01-23 4 views
3

나는 가장 긴 공통적 인 하위 문자열을 찾기 위해 코드를 작성하라고 요청한 학교에 대해이 임무를 부여 받았습니다. 나는 그렇게 해왔지만 너무 크지 않은 텍스트에서만 작동하며 Moby Dick과 War And Peace의 일반적인 하위 문자열을 찾아야합니다. 내가 잘못하고있는 것의 올바른 방향으로 나를 지적 할 수 있다면, 고맙겠습니다. 컴파일러는 나에게에서 OutOfMemory대문자로 가장 긴 공통 부분 문자열

package datastructuresone; 

import java.io.File; 
import java.io.FileNotFoundException; 
import java.util.Arrays; 
import java.util.Scanner; 


class SuffixArray 
{ 

private final MyString[] suffixes; 
private final int N; 

public SuffixArray(String s) 
{ 
    N = s.length(); 
    MyString snew = new MyString(s); 
    suffixes = new MyString[N]; 
    for (int i = 0; i < N; i++) 
    { 
     suffixes[i] = snew.substring(i); 
    } 
    Arrays.sort(suffixes); 
} 

public int length() 
{ 
    return N; 
} 

public int index(int i) 
{ 
    return N - suffixes[i].length(); 
} 

public MyString select(int i) 
{ 
    return suffixes[i]; 
} 

// length of longest common prefix of s and t 
private static int lcp(MyString s, MyString t) 
{ 
    int N = Math.min(s.length(), t.length()); 
    for (int i = 0; i < N; i++) 
    { 
     if (s.charAt(i) != t.charAt(i)) 
     { 
      return i; 
     } 
    } 
    return N; 
} 

// longest common prefix of suffixes(i) and suffixes(i-1) 
public int lcp(int i) 
{ 
    return lcp(suffixes[i], suffixes[i - 1]); 
} 

// longest common prefix of suffixes(i) and suffixes(j) 
public int lcp(int i, int j) 
{ 
    return lcp(suffixes[i], suffixes[j]); 

} 
} 

public class DataStructuresOne 
{ 

public static void main(String[] args) throws FileNotFoundException 
{ 
    Scanner in1 = new Scanner(new File("./build/classes/WarAndPeace.txt")); 
    Scanner in2 = new Scanner(new File("./build/classes/MobyDick.txt")); 

    StringBuilder sb = new StringBuilder(); 
    StringBuilder sb1 = new StringBuilder(); 

    while (in1.hasNextLine()) 
    { 
     sb.append(in1.nextLine()); 

    } 
    while (in2.hasNextLine()) 
    { 
     sb1.append(in2.nextLine()); 
    } 

    String text1 = sb.toString().replaceAll("\\s+", " "); 
    String text2 = sb1.toString().replaceAll("\\s+", " "); 

    int N1 = text1.length(); 
    int N2 = text2.length(); 

    SuffixArray sa = new SuffixArray(text1 + "#" + text2); 
    int N = sa.length(); 

    String substring = ""; 
    for (int i = 1; i < N; i++) 
    { 

     // adjacent suffixes both from second text string 
     if (sa.select(i).length() <= N2 && sa.select(i - 1).length() <= N2) 
     { 
      continue; 
     } 

     // adjacent suffixes both from first text string 
     if (sa.select(i).length() > N2 + 1 && sa.select(i - 1).length() > N2 + 1) 
     { 
      continue; 
     } 

     // check if adjacent suffixes longer common substring 
     int length = sa.lcp(i); 
     if (length > substring.length()) 
     { 
      substring = sa.select(i).toString().substring(0, length); 
      System.out.println(substring + " "); 
     } 
    } 

    System.out.println("The length of the substring " + substring.length() + "length on first N " + N1 + " length of Second N " + N2 
      + "The length of the array sa: " + N); 
    System.out.println("'" + substring + "'"); 

    final class MyString implements Comparable<MyString> 
{ 

public MyString(String str) 
{ 
    offset = 0; 
    len = str.length(); 
    arr = str.toCharArray(); 
} 

public int length() 
{ 
    return len; 
} 

public char charAt(int idx) 
{ 
    return arr[ idx + offset]; 
} 

public int compareTo(MyString other) 
{ 
    int myEnd = offset + len; 
    int yourEnd = other.offset + other.len; 
    int i = offset, j = other.offset; 

    for (; i < myEnd && j < yourEnd; i++, j++) 
    { 
     if (arr[ i] != arr[ j]) 
     { 
      return arr[ i] - arr[ j]; 
     } 
    } 

    // reached end. Who got there first? 
    if (i == myEnd && j == yourEnd) 
    { 
     return 0; // identical strings 
    } 
    if (i == myEnd) 
    { 
     return -1; 
    } else 
    { 
     return +1; 
    } 
} 


public MyString substring(int beginIndex, int endIndex) 
{ 
    return new MyString(arr, beginIndex + offset, endIndex - beginIndex); 
} 

public MyString substring(int beginIndex) 
{ 
    return substring(beginIndex, offset + len); 
} 

public boolean equals(Object other) 
{ 
    return (other instanceof MyString) && compareTo((MyString) other) == 0; 
} 

public String toString() 
{ 
    return new String(arr, offset, len); 
} 

private MyString(char[] a, int of, int ln) 
{ 
    arr = a; 
    offset = of; 
    len = ln; 
} 
private char[] arr; 
private int offset; 
private int len; 
} 

답변

1

을주고, 그 너무 큰 말을하는 이유는 접미사 배열을 만들 호출하지만 나도 몰라 할 때 오류가 MyString에 클래스의 문자열 방법에 있다고 불평 나는 적어도 3 개 사본을 계산 코드의 처음 몇 줄에 두 텍스트의 다음은 몇 가지 아이디어입니다.

  • 각 줄을 읽을 때 공백을 변환합니다. 거대한 문자열이 아니라. 줄의 앞과 끝에 공백이있는 경우를 잊지 마십시오.
  • String 대신 StringBuilder를 사용하여 MyString 클래스를 빌드하십시오. 가능한 경우 고유 메소드를 사용하여 StringBuilder 내부를 모두 살펴보십시오.
  • 문자열을 더 이상 추출하지 마십시오.

-Xmx java runtime 옵션을 찾아 기본값보다 큰 힙 공간을 설정하십시오. 내가 외워 두지 않았 으면 이걸 써야 해. 마지막으로 -Xmx = 1024M에 M이 필요하다는 것을 주목하십시오. (두 책이 얼마나 큰 볼 수있는 파일 크기 좀 봐.) 여기

3

: 당신은뿐만 아니라 전체 긴 문자열을 저장하기 위해 노력하고 있지만, 전체 문자열된다

for (int i = 0; i < N; i++) 
{ 
    suffixes[i] = snew.substring(i); 
} 

-1 편지, 전체 문자열 - 2 글자 등.이 모든 것은 별도로 저장됩니다.

문자열이 10자인 경우 10 개의 다른 문자열에 총 55자를 저장할 수 있습니다.

1000 자에서 총 500500자를 저장하고 있습니다.

보다 일반적으로 길이 * (길이 + 1)/2자를 처리해야합니다.


그냥 재미를 위해, 나는 전쟁과 평화에 얼마나 많은 문자 모르겠지만, 페이지 주변에 1250 카운트, 250 인 일반적인 단어/페이지 추정, 평균 단어는 약 5 문자 인 (1250 * 250 * 5) * (1250 * 250 * 5 + 1)/2 = 1.2207039 * 10^12 자입니다.

메모리의 char 크기는 2 바이트이므로 크기가 약 2.22 TB입니다 (소설 텍스트의 경우 1.49 MB와 비교).

0

MyString을 구성하면 arr = str.toCharArray();을 호출하여 문자열의 문자 데이터 복사본을 새로 만듭니다. 하지만 Java에서는 문자열이 변경되지 않습니다. 따라서 데이터 사본 대신 문자열에 대한 참조를 저장하지 않으시겠습니까?

한 번에 모든 접미어를 구성하지만 한 번에 하나씩 (잘, 2 개) 만 참조하십시오. 현재 신경 써야하는 접미어를 참조하도록 솔루션을 다시 코딩하고 필요할 때만 구성하고 나중에 참조를 잃어 버리면 Java에서 가비지 수집 할 수 있습니다. 이렇게하면 메모리가 부족해질 가능성이 줄어 듭니다.문자열 2 개를 저장하는 메모리 오버 헤드를 비교하면 수십만 개의 문자열을 저장할 수 있습니다.

0

이 프로그램은 Scala에서 작성했습니다. 어쩌면 당신은 그것을 자바로 번역 할 수 있습니다.

class MyString private (private val string: String, startIndex: Int, endIndex: Int) extends Comparable[MyString] { 
    def this(string: String) = this(string, 0, string.length) 
    def length() = endIndex-startIndex 
    def charAt(i: Int) = { 
    if(i >= length) throw new IndexOutOfBoundsException 
    string.charAt(startIndex + i) 
    } 
    def substring(start: Int, end: Int): MyString = { 
    if(start < 0 || end > length || end < start) throw new IndexOutOfBoundsException 
    new MyString(string, startIndex + start, startIndex + end) 
    } 
    def substring(start: Int): MyString = substring(start, length) 
    def longestCommonSubstring(other: MyString): MyString = { 
    var index = 0 
    val len = math.min(length, other.length) 
    while(index < len && charAt(index) == other.charAt(index)) index += 1 
    substring(0, index) 
    } 
    def compareTo(other: MyString): Int = { 
    val len = math.min(length, other.length) 
    for(i <- 0 until len) { 
     if(charAt(i) > other.charAt(i)) return 1 
     if(charAt(i) < other.charAt(i)) return -1 
    } 
    length-other.length 
    } 
    def >(other: MyString) = compareTo(other) > 0 
    def <(other: MyString) = compareTo(other) < 0 
    override def equals(other: Any) = other.isInstanceOf[MyString] && compareTo(other.asInstanceOf[MyString]) == 0 
    override def toString() = "\"" + string.substring(startIndex, endIndex) + "\"" 
} 

def readFile(name: String) = new MyString(io.Source.fromFile(name).getLines.mkString(" ").replaceAll("\\s+", " ")) 
def makeList(str: MyString) = (0 until str.length).map(i => str.substring(i)).toIndexedSeq 

val string1 = readFile("WarAndPeace.txt") 
val string2 = readFile("MobyDick.txt") 

val (list1, list2) = (makeList(string1).sorted, makeList(string2).sorted) 

var longestMatch = new MyString("") 
var (index1, index2) = (0,0) 
while(index1 < list1.size && index2 < list2.size) { 
    val lcs = list1(index1).longestCommonSubstring(list2(index2)) 
    if(lcs.length > longestMatch.length) longestMatch = lcs 
    if(list1(index1) < list2(index2)) index1 += 1 
    else index2 += 1 
} 

println(longestMatch) 
관련 문제