有没有一种比O(n)更快的方法来反转一个字符串?

huangapple go评论76阅读模式
英文:

Is there a way to reverse a string faster than O(n)?

问题

以下是翻译好的内容:

我有以下的代码,使用参数-Xmx<1024M>运行时间超过5秒。

我知道for循环需要O(q)的时间,同时reverse()toString()每个都需要O(n)的时间。

有没有办法在少于O(n)的时间内反转字符串?还是有其他因素在减慢代码速度?任何帮助将不胜感激!

class Main {
  public static void main(String[] args){
    String s = "a";
    String qa = "200000";
    int q = Integer.parseInt(qa);
    String[] t = new String[q];
    for(int i = 0; i < q; i++) {
      if(i%2==0) {t[i] = "2 1 x";}
      if(i%2==1) {t[i] = "1";}
      if(t[i].toCharArray()[0] == '1') {
        StringBuilder rev = new StringBuilder(s).reverse();
        s = rev.toString();
      } else {
        char letter = t[i].toCharArray()[4];
        if(t[i].toCharArray()[2] == '1') {
          s = letter + s;
        } else {
          s = s + letter;
        }
      }
    }
    System.out.println(s);
  }
}
英文:

I have the following code which takes more than 5 seconds to run with the argument -Xmx&lt;1024M&gt;.

I am aware that the for loop takes O(q) time, as well as the reverse() and toString() take O(n) time each.

Is there a way to reverse the string in less than O(n) time? Or is something else slowing the code down? Any help would be welcome!

class Main {
  public static void main(String[] args){
    String s = &quot;a&quot;;
    String qa = &quot;200000&quot;;
    int q = Integer.parseInt(qa);
    String[] t = new String[q];
    for(int i = 0; i &lt; q; i++) {
      if(i%2==0) {t[i] = &quot;2 1 x&quot;;}
      if(i%2==1) {t[i] = &quot;1&quot;;}
      if(t[i].toCharArray()[0] == &#39;1&#39;) {
        StringBuilder rev = new StringBuilder(s).reverse();
        s = rev.toString();
      } else {
        char letter = t[i].toCharArray()[4];
        if(t[i].toCharArray()[2] == &#39;1&#39;) {
          s = letter + s;
        } else {
          s = s + letter;
        }
      }
    }
    System.out.println(s);
  }
}

答案1

得分: 4

无论它应该做什么(我不知道),我发现了以下问题:

  • 每次迭代中都有多个 StringBuilder 的实例化。
  • 使用 + 运算符进行字符串连接。
  • 反复使用 Sring::toCharArray(请查看第二个解决方案)。

您将使用仅直接使用一个 StringBuilder 实例来获得更快的结果:

String s = "a";
String qa = "200000";
int q = Integer.parseInt(qa);
String[] t = new String[q];
StringBuilder sb = new StringBuilder(s);       // 在循环之前实例化
for (int i = 0; i < q; i++) {
    if(i%2==0) {t[i] = "2 1 x";}
    if(i%2==1) {t[i] = "1";}
    if(t[i].toCharArray()[0] == '1') {
        sb.reverse();                          // 这里您所做的只是颠倒 's'
    } else {
        char letter = t[i].toCharArray()[4];
        if(t[i].toCharArray()[2] == '1') {
            sb.insert(0, letter);              // 在前面添加一个字符
        } else {
            sb.append(letter);                 // 在后面添加一个字符
        }
    }
}

另一件事是您多次定义一个字符串,例如 t[i] = "2 1 x";,然后与 t[i].toCharArray()[0] 进行比较。预先定义这些不可变值并使用 char[][] 也应该有所帮助:

String s = "a";
String qa = "200000";
int q = Integer.parseInt(qa);
char[][] t = new char[q][];                    // 使用 char[][] 代替 String[]
char[] char21x = new char[]{'2', '1', 'x'};    // 预定义的数组
char[] char1 = new char[]{'1'};                // 另一个预定义的数组
StringBuilder sb = new StringBuilder(s);       // 在循环之前实例化
for (int i = 0; i < q; i++) {
    if(i%2==0) {t[i] = char21x;}     // 第一次重用
    if(i%2==1) {t[i] = char1;}       // 第二次重用
    if(t[i][0] == '1') {             // 使用 char[][] 代替 String::toCharArray,请注意索引
        sb.reverse();                // 这里您所做的只是颠倒 's'
    } else {
        char letter = t[i][2];       // 使用 char[][] 代替 String::toCharArray,请注意索引
        if(t[i][1] == '1') {
            sb.insert(0, letter);    // 在前面添加一个字符
        } else {
            sb.append(letter);       // 在后面添加一个字符
        }
    }
}

编辑: 我已经在我的笔记本电脑上使用了最简单的方式测试了该解决方案,使用了 System.currentTimeMillis() 的差值:

  • 原始解决方案:7.6586.8997.046
  • 第二个解决方案:3.2883.6913.158
  • 第三个解决方案:2.7172.9662.717

结论: 就计算复杂性本身而言,我看不到任何改进的方法,但使用正确的方式处理字符串可以将时间复杂性降低 2-3 倍(在我的情况下)。

一般建议: 您可以在循环之前实例化和定义的内容,在循环之前就这样做。

英文:

Regardless of what is it supposed to do (I have no idea), I found the following problems:

  • Multiple instantinations of StringBuilder in each iteration.
  • String concatenation using + operator.
  • Repetitive usage of Sring::toCharArray (see the 2nd solution)

You will achieve a faster result using directly only one instance of StringBuilder:

String s = &quot;a&quot;;
String qa = &quot;200000&quot;;
int q = Integer.parseInt(qa);
String[] t = new String[q];
StringBuilder sb = new StringBuilder(s);       // Instantiate before the loop
for (int i = 0; i &lt; q; i++) {
	if(i%2==0) {t[i] = &quot;2 1 x&quot;;}
	if(i%2==1) {t[i] = &quot;1&quot;;}
	if(t[i].toCharArray()[0] == &#39;1&#39;) {
		sb.reverse();                          // all you did here is just reversing &#39;s&#39;
	} else {
		char letter = t[i].toCharArray()[4];
		if(t[i].toCharArray()[2] == &#39;1&#39;) {
			sb.insert(0, letter);              // prepend a letter
		} else {
			sb.append(letter);                 // append a letter
		}
	}
}

Another thing is that you multiple times define a String such as t[i] = &quot;2 1 x&quot;; and then you compare with t[i].toCharArray()[0]. Pre-definig these immutable values and using char[][] should help too:

String s = &quot;a&quot;;
String qa = &quot;200000&quot;;
int q = Integer.parseInt(qa);
char[][] t = new char[q][];                    // char[][] instead of String[]
char[] char21x = new char[]{&#39;2&#39;, &#39;1&#39;, &#39;x&#39;};    // predefined array
char[] char1 = new char[]{&#39;1&#39;};                // another predefined array
StringBuilder sb = new StringBuilder(s);       // Instantiate before the loop
for (int i = 0; i &lt; q; i++) {
	if(i%2==0) {t[i] = char21x;}     // first reuse
	if(i%2==1) {t[i] = char1;}       // second reuse
	if(t[i][0] == &#39;1&#39;) {             // instead of String::toCharArray, mind the indices
		sb.reverse();                // all you did here is just reversing &#39;s&#39;
	} else {
		char letter = t[i][2];       // instead of String::toCharArray, mind the indices
		if(t[i][1] == &#39;1&#39;) {
			sb.insert(0, letter);    // prepend a letter
		} else {
			sb.append(letter);       // append a letter
		}
	}
}

Edit: I have tested the solution with the simplest way possible using a difference of System.currentTimeMillis() on my laptop:

  • Original solution: 7.658, 6.899 and 7.046 seconds
  • 2nd solution: 3.288, 3.691 and 3.158 seconds
  • 3rd solution: 2.717, 2.966 and 2.717 seconds

Conclusion: I see no way to improve the algorithm itself in terms of the computation complexity, however, using the correct ways to treat Strings helps to reduce the time complexity 2-3 times (in my case).

General advice: What you can instantiate and define before the loop, do it before the loop.

答案2

得分: -1

没有办法在小于O(n)的时间内反转一个字符串:产生大小为n的输出的程序必然至少需要O(n)的时间。

您的代码包含许多不必要的操作,导致程序运行变慢。该程序生成了50000个字母x,然后是一个字母a,然后又是另外50000个字母x。以下是同样功能的更快(而且更容易理解)的实现。

class Faster {
    public static void main(String[] args) {

        String hundredXs = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

        for (int i = 0; i < 500; i++)
            System.out.print(hundredXs);

        System.out.print("a");

        for (int i = 0; i < 500; i++)
            System.out.print(hundredXs);

        System.out.println();
    }
}
英文:

> Is there a way to reverse the string in less than O(n) time? Or is something else slowing the code down?

No there is no way to reverse a string in less than O(n) time: A program that produces an output of size n necessarily takes o(n) time at the minimum.

Your code has lots of unnecessary operations that slow the program down. The program produces 50000 letters x, followed by one letter a, followed by another 50000 letters x. Here is a much faster (and easier to understand) implementation of the same program.

class Faster {
    public static void main(String[] args) {

        String hundredXs = &quot;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&quot;;

        for (int i = 0; i &lt; 500; i++)
            System.out.print(hundredXs);

        System.out.print(&quot;a&quot;);

        for (int i = 0; i &lt; 500; i++)
            System.out.print(hundredXs);

        System.out.println();
    }
}

huangapple
  • 本文由 发表于 2020年4月4日 05:38:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/61020809.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定