Java 递归字符串连接

Java 递归字符串连接,java,recursion,Java,Recursion,我正在处理一个需要递归连接字符串的问题,结果遇到了一个问题 问题指出,s(0)=0,s(1)=1,s(n)=s(n-1)s(n-2)表示n>=2,其中s(n)是前两个字符串的串联字符串 输入将指示有多少个(n,k)对实例将作为第一个整数输入,后跟包含非负整数的每一行 n(0因为您是在Java中进行此操作的,而不是在带有尾部调用优化的FP语言中进行此操作,所以有两件事是错误的:递归和字符串连接。在Java中,您需要使用可变生成器进行迭代和字符串构建 具体地说,递归函数保留在生成完整字符串的过程中生

我正在处理一个需要递归连接字符串的问题,结果遇到了一个问题

问题指出,
s(0)=0,s(1)=1,s(n)=s(n-1)s(n-2)表示n>=2
,其中
s(n)
是前两个字符串的串联字符串

输入将指示有多少个
(n,k)
对实例将作为第一个整数输入,后跟包含非负整数的每一行
n(0因为您是在Java中进行此操作的,而不是在带有尾部调用优化的FP语言中进行此操作,所以有两件事是错误的:递归和字符串连接。在Java中,您需要使用可变生成器进行迭代和字符串构建


具体地说,递归函数保留在生成完整字符串的过程中生成的所有中间字符串。这就是O(n2)内存使用。

因为您是在Java中进行此操作的,而不是在带有尾部调用优化的FP语言中,所以有两件事是错误的:递归和字符串连接。在Java中,您需要使用可变生成器进行迭代和字符串构建


具体地说,递归函数会保留在生成完整字符串的过程中生成的所有临时字符串。这就是O(n2)内存使用量。

这种方法的问题在于它创建了太多的废弃字符串。每次编写

return str1 + str2;
创建了一个新的
字符串
对象。此类丢弃对象的数量随
n
线性增长,而其总长度增长为O(n2)

您有两种解决此问题的方法:

  • 保持程序线性,并在顶层传递
    StringBuilder
    。每次递归调用都将调用
    append
    ,而不是使用运算符
    +
    进行连接
  • 使用-由于需要计算的字符串数量很小,因此存储到目前为止计算的字符串并重新使用它们应该可以解决问题

问题限制的一个更大问题是,它的输出不能放入
字符串中:Java允许长度最多为231的字符串,而60的代码的输出要长一点,即1548008755920个字符。虽然您应该能够将此输出保存在文件中,但无法将其存储为
字符串
>有或没有备忘录。

你的方法的问题是,它创建了太多的废弃字符串。每次你写

return str1 + str2;
创建了一个新的
字符串
对象。此类丢弃对象的数量随
n
线性增长,而其总长度增长为O(n2)

您有两种解决此问题的方法:

  • 保持程序线性,并在顶层传递
    StringBuilder
    。每次递归调用都将调用
    append
    ,而不是使用运算符
    +
    进行连接
  • 使用-由于需要计算的字符串数量很小,因此存储到目前为止计算的字符串并重新使用它们应该可以解决问题

问题限制的一个更大问题是,它的输出不能放入
字符串中:Java允许长度最多为231的字符串,而60的代码的输出要长一点,即1548008755920个字符。虽然您应该能够将此输出保存在文件中,但无法将其存储为
字符串
>,有或没有备忘录。

嗯,老实说,我觉得这像是斐波那契,所以我的方法看起来有点像

public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
    System.out.println(stringbonacci(i));
}
}

private static String stringbonacci(int i) {
if (i == 0) {
    return "0";
}
if (i == 1) {
    return "1";
} else {
    return stringbonacci(i - 1) + stringbonacci(i - 2);
}
}
publicstaticvoidmain(字符串[]args){
对于(int i=0;i<6;i++){
系统输出println(stringbonacci(i));
}
}
私有静态字符串stringbonacci(int i){
如果(i==0){
返回“0”;
}
如果(i==1){
返回“1”;
}否则{
返回stringbonacci(i-1)+stringbonacci(i-2);
}
}
结果如下:

0

一,

十,

101

10110

10110101


老实说,我觉得这像斐波那契,所以我的方法有点像

public static void main(String[] args) {
for (int i = 0; i < 6; i++) {
    System.out.println(stringbonacci(i));
}
}

private static String stringbonacci(int i) {
if (i == 0) {
    return "0";
}
if (i == 1) {
    return "1";
} else {
    return stringbonacci(i - 1) + stringbonacci(i - 2);
}
}
publicstaticvoidmain(字符串[]args){
对于(int i=0;i<6;i++){
系统输出println(stringbonacci(i));
}
}
私有静态字符串stringbonacci(int i){
如果(i==0){
返回“0”;
}
如果(i==1){
返回“1”;
}否则{
返回stringbonacci(i-1)+stringbonacci(i-2);
}
}
结果如下:

0

一,

十,

101

10110

10110101


我强烈建议缓存每个方法调用的结果,这样您就不必多次重新计算所有内容。这将以少量堆为代价极大地提高您的性能。类似这样的事情

public class RecursiveString {
    private static final Map<Integer, String> cache = new HashMap<>();
    static {
        cache.put(0, "0");
        cache.put(1, "1");
    }

    public static String generateString(Integer i) {
        if (i < 0) return generateString(0); // or something... avoid negatives.

        if (cache.get(i) != null) return cache.get(i); // cache hit.

        String generated = String.format("%s%s", 
            generateString(i-1), generateString(i-2));
        cache.put(i, generated);

        return generated;
    }
}
公共类递归字符串{
私有静态最终映射缓存=新HashMap();
静止的{
cache.put(0,“0”);
cache.put(1,“1”);
}
公共静态字符串生成器(整数i){
如果(i<0)返回generateString(0);//或其他什么…请避免使用负片。
if(cache.get(i)!=null)返回cache.get(i);//缓存命中。
生成的字符串=String.format(“%s%s”,
发电限制(i-1)、发电限制(i-2));
cache.put(i,生成);
产生的回报;
}
}

请注意,在2G堆上,generateString在我的机器上通过i=40工作,这仅仅是由于生成字符串的长度。字符串的长度将是
2*fib(i)
字节大小,因此一旦您开始访问这些索引,您的内存需求将迅速爆炸。

我强烈建议缓存每个方法调用的结果,这样您就不必多次重新计算所有内容。这将以少量堆为代价大大提高性能。类似于s

public class RecursiveString {
    private static final Map<Integer, String> cache = new HashMap<>();
    static {
        cache.put(0, "0");
        cache.put(1, "1");
    }

    public static String generateString(Integer i) {
        if (i < 0) return generateString(0); // or something... avoid negatives.

        if (cache.get(i) != null) return cache.get(i); // cache hit.

        String generated = String.format("%s%s", 
            generateString(i-1), generateString(i-2));
        cache.put(i, generated);

        return generated;
    }
}
公共类递归字符串{
私有静态最终映射缓存=新HashMap();
静止的{
cache.put(0,“0”);
cache.put(1,“1”);
}
公共静态字符串生成器(整数i)
public class RecursiveString {
    private static final Map<Integer, String> cache = new HashMap<>();
    static {
        cache.put(0, "0");
        cache.put(1, "1");
    }

    public static String generateString(Integer i) {
        if (i < 0) return generateString(0); // or something... avoid negatives.

        if (cache.get(i) != null) return cache.get(i); // cache hit.

        String generated = String.format("%s%s", 
            generateString(i-1), generateString(i-2));
        cache.put(i, generated);

        return generated;
    }
}
import java.util.Scanner;

public class BinaryConcat {
    private String[] dict;

    public BinaryConcat(int n) {
        if (n < 0) throw new IllegalArgumentException();
        dict = new String[2 + n]; // Have 2 base cases

        dict[0] = "0";
        dict[1] = "1";
    }

    public String getValue(int n) {
        if (n < 0) throw new IllegalArgumentException();
        if (n <= 1) return dict[n];
        if (dict[n] == null || dict[n].isEmpty()) {
            dict[n] = String.format("%s%s", getValue(n - 1), getValue(n - 2));
        }

        return dict[n];
    }

    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);

        int lines = input.nextInt();
        input.nextLine(); // consume linefeed

        for (int i = 0; i < lines; i++) {
            String line = input.nextLine();
            String[] nk = line.split(" ");
            int n = Integer.parseInt(nk[0]);
            int k = Integer.parseInt(nk[1]);

            String value = new BinaryConcat(n).getValue(n);
            System.out.println(value.charAt(k - 1));
        }

    }
}