2015-05-15 52 views
0

问题 - 按照字典顺序排列给定字符串的所有不同子字符串并连接它们。打印连接字符串的第K个字符。可以肯定的是,给定的K值将是有效的,即将有第K个字符不同子串的拼接

输入格式 第一行将包含数字T即测试用例的数量。 每个测试用例的第一行包含一个包含字符串的字符(A-Z)和第二线将包含许多K.

输出格式 打印第K个字符(串1索引)

约束 1≤T≤5 1≤length≤105 K将是一个适当的整数。

采样输入#00

1 
dbac 
3 

样本输出#00

c 

说明#00

的子串布置在词典顺序时如下

一个,交流,b,ba,bac,c,d,db,dba,dbac 关于concate给他们,我们得到

aacbbabaccddbdbadbac 这个字符串中的第三个字符是c,因此答案。

这是我的代码:

import java.io.*; 
import java.util.*; 
import java.text.*; 
import java.math.*; 
import java.util.regex.*; 

public class Solution 
{ 

public static void gen(String str,int k) 
{ 


     int i,c;ArrayList<String>al=new ArrayList<String>(); 
    for(c=0;c<str.length();c++) 
    { 
     for(i=1;i<=str.length()-c;i++) 
     { 
      String sub = str.substring(c,c+i); 
      al.add(sub); 
     } 
    } 

    HashSet hs = new HashSet(); 
    hs.addAll(al); 
    al.clear(); 
    al.addAll(hs); 

    String[] res = al.toArray(new String[al.size()]); 
    Arrays.sort(res); 

    StringBuilder sb= new StringBuilder(); 

     for(String temp:res) 
     { 
      sb.append(temp); 
     } 

    String s = sb.toString(); 
    System.out.println(s.charAt(k-1)); 
} 


public static void main(String[] args) 
{ 
    Scanner sc = new Scanner (System.in); 
    int t = Integer.parseInt(sc.nextLine()); 

     while((t--)>0) 
     { 
      String str = sc.nextLine(); 
      int k = Integer.parseInt(sc.nextLine());     
      gen(str,k); 

     } 

    } 
} 

此代码工作的很好像上面的测试情况下投入较小,但对大输入的其超时或显示这样的事情我明白这个问题是与记忆,任何替代方法来做这个问题或反正重复使用相同的内存?

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
at java.util.Arrays.copyOfRange(Arrays.java:2694) 
at java.lang.String.<init>(String.java:203) 
at java.lang.String.substring(String.java:1913) 
at Solution.gen(Solution.java:19) 
at Solution.main(Solution.java:54) 

回答

1

在给出的约束条件下(最多105个字符),你不应该有内存不足的问题。也许你正在用非常大的字符串进行测试。

所以,如果你有,这里有一些地方,你是在浪费内存:

  • 您填写的设置后,你把它复制到你的列表中。这意味着子串集合的两个副本,而你不打算再使用这个集合。
  • 将列表复制到数组后,您现在有三个子串集合的副本,尽管您不打算再使用该列表。
  • 现在您创建一个StringBuilder并将所有子字符串放入其中。但了解整个串联字符串并不是很有趣。我们只需要一个字符,那为什么要把这个连接放在内存中呢?另外,在上面所有浪费的副本中,至少你没有复制子字符串本身。但是现在您将它们追加到StringBuilder,您正在创建它们的副本。这将是一个非常长的字符串。
  • 然后通过使用toString()StringBuilder的内容复制到新字符串中。这创建了一个非常大的连接字符串的副本(我们已经说过我们并不需要它)。

您已经有了一个使用TreeSet并直接填充它的合理建议,而不是创建列表,集合和排序列表。下一步是从该集合中提取正确的字符,而实际上并未将连接字符串保留在左右。

因此,假设您的集合称为set

Iterator<String> iter = set.iterator(); 

int lengthSoFar = 0; 
String str = null; 

while (lengthSoFar < k && iter.hasNext()) { 

    str = iter.next();   // Got the next substring; 
    lengthSoFar += str.length(); 
} 

// At this point we have the substring where we expect the k'th 
// character to be. 

System.out.println(str.charAt(k - lengthSoFar + str.length() - 1); 

注意,这将需要程序更长的时间才能到达的k比低值高值,但通常会比建筑串联整个快字符串,因为只要你得到正确的子字符串,你就会停下来。

1

您的内存不足。您可以通过使用-Xms256m -Xmx1024启动JVM来增加JVM使用的内存,并且可以尝试一些优化。

public static void gen(String str, int k) { 

    int i, c; 

    //Adding directly to the Set prevents a larger list because you remove the duplicates 
    Set<String> set = new TreeSet<String>(); 

    for (c = 0; c < str.length(); c++) { 
     for (i = 1; i <= str.length() - c; i++) { 
      String sub = str.substring(c, c + i); 
      set.add(sub); 
     } 
    } 
    //TreeSet already orders by the String comparator 


    StringBuilder sb = new StringBuilder(); 

    for (String temp : set) { 
     sb.append(temp); 
     if(sb.length()>k){ 
      break; 
     } 
    } 

    String s = sb.toString(); 
    System.out.println(s.charAt(k - 1)); 
} 

[编辑]增加了小的性能提升。试着看看它是否变快,我没有看StringBuilder.length()的性能,看它是否会改善或减少。

+0

即使在使用此代码之后,它也需要超过4秒的时间才能编译,所以测试用例没有通过,但仍然为此代码添加坦克,我不需要将它们添加到列表和哈希集合中,并且不需要排序... i可以直接使用:) – coder101

+0

所以我的回答是正确的。如果你使用的是正确的编程比赛,可能还不够。对? – gfelisberto

+0

是的,但不幸的是,它无法帮助我通过测试用例 – coder101