我的程序超时大字符串,我怎么能让它更快?

时间:2017-02-01 01:03:22

标签: java algorithm

我试图扭转字符串的元音。我的程序有效,但速度很慢,并且在大型字符串上超时。我怎么能让它更快? 编写一个函数,该函数将字符串作为输入,并仅反转字符串的元音。

示例1:给定s = "hello"return "holle"

示例2:给定s = "leetcode"return "leotcede"

注意: 元音不包括字母“y”。

public class Solution {
    public String reverseVowels(String s){

        Set<Character> vowel = new HashSet<>();

        char [] sArray = s.toCharArray();
        vowel.add('a');
        vowel.add('e');
        vowel.add('o');
        vowel.add('i');
        vowel.add('u');
        vowel.add('A');
        vowel.add('E');
        vowel.add('I');
        vowel.add('O');
        vowel.add('U');

        Stack<Character> v = new Stack<>();
        String temp = "";

        int i = 0;
        for (Character c: sArray){
            if (vowel.contains(c)){
                v.push(c);

            }
            i++;
        }
        for (Character c: sArray){
            if (vowel.contains(c)){
               if (!v.empty())
                c = v.pop();
            }
            temp+= c;

        }
        return temp;

    }
}

编辑:我已经更改了我的代码以使用StringBuilder,现在它传递了测试用例,但它仍然比95%的Java解决方案慢,所以我将进行其他更改以尝试使其更快。程序的缓慢并不完全是因为没有使用StringBuilder。即使有了这个改变,我的程序仍然很慢。这不是其他问题的重复。

4 个答案:

答案 0 :(得分:3)

这里有一些事情发生了:

  1. 每次使用该方法时,都会重新创建元音的Set,并且每次都会将元音添加到元音中(这也可能涉及扩展集合的大小);因为它们不会改变,所以没有必要。 (这没有太大的区别,但我想我会提到它。)

  2. 每个char原语都会被自动装入Character对象中 - 在您的两个循环中以及添加到元音的Stack时。

  3. 由于Tom提供的可能重复建议,您可能会从StringBuilder.append(char)而不是+运营商获得更好的效果。

  4. 脱离我的头脑,避免上述缺陷的更好的实现可能看起来像:

    private static boolean isVowel(char c) {
        switch (c) {
            case 'a':
            case 'e':
            case 'i':
            case 'o':
            case 'u':
            case 'A':
            case 'E':
            case 'I':
            case 'O':
            case 'U':
                return true;
            default: return false;
        }
    }
    
    public static String reverseVowels(String s) {
        final char [] sArray = s.toCharArray();
        final StringBuilder reversedString = new StringBuilder();
        final StringBuilder vowels = new StringBuilder();
    
        int vowelIndex = -1;
        for (char c : sArray) {
            if (isVowel(c)) {
                vowels.append(c);
                ++vowelIndex;
            }
        }
    
        for (char c : sArray) {
            if (isVowel(c))
                c = vowels.charAt(vowelIndex--);
    
            reversedString.append(c);
        }
        return reversedString.toString();
    }
    

答案 1 :(得分:1)

正如Tom在他的评论中所建议的,主要问题可能是你使用的是String而不是StringBuilder,例如参见here。尝试将temp更改为新的StringBuilder,并以此方式构建字符串。如果运气好的话,那将会有所不同。或者,使用David的方法,只需更改char数组中的字符,然后对其进行最终的toString。

否则,如果它仍然不够快,而不是通过整个字符串两次,你可能会注意到你的第一次通过时字符串中每个元音的位置(可能使用队列整数?),然后快速迭代元音位置,用你堆栈上的下一个元音替换每个点的字母。请注意,我不确定这一定会更快,但可能值得一试。这种方法的一个潜在优势可能是您可以在用完所有元音后停止处理字符串。例如,如果你有一个类似&#34; aeiouzx ...&#34;的字符串,只有元音出现在前面,一旦你完成了元音列表,你就会认识你完成后可以停在那里,可能避免浪费时间。可能是对特定字符串的改进。

希望其中一个可以将您的表现提升到理想的水平。

答案 2 :(得分:1)

这是我的例子(确保你为JVM提供了大量的堆)。

翻转元音为3.2亿个字符需要719ms才能在我的机器上运行。 该解决方案使用两个索引将元音交换到元组中。

new String(c)不是最有效的内存,它会复制数组以及String.toCharArray 但是,这个很快。

public class Main {

    public static void main(String[] args) {
        Main m = new Main();
        System.out.println(m.reverseVowels("hello"));
        System.out.println(m.reverseVowels("leetcode"));
        char[] longString = new char[320_000_000];
        Arrays.fill(longString, 'e');
        String loooooongString = new String(longString);
        long t = System.currentTimeMillis();
        m.reverseVowels(loooooongString);
        System.out.println("Duration:" + (System.currentTimeMillis() - t));
    }

    String reverseVowels(String s) {
        char[] c = s.toCharArray();
        int i = 0;
        int j = c.length - 1;
        char tmp;
        while (i < j) {
            while (i < j && !isVowel(c[i])) i++;
            while (i < j && !isVowel(c[j])) j--;
            if (i >= j) break;
            tmp = c[i];
            c[i] = c[j];
            c[j] = tmp;
            i++;
            j--;
        }
        return new String(c);
    }

    boolean isVowel(char c) {
        switch (c) {
            case 'a':
            case 'e':
            case 'i':
            case 'o':
            case 'u':
            case 'A':
            case 'E':
            case 'O':
            case 'U':
                return true;
            default:
                return false;
        }
    }
}

答案 3 :(得分:1)

我不确定性能,但试试这个:

    Set<Character> vowels = new HashSet<>();
    vowels.add('a');
    vowels.add('e');
    vowels.add('o');
    vowels.add('i');
    vowels.add('u');
    vowels.add('A');
    vowels.add('E');
    vowels.add('I');
    vowels.add('O');
    vowels.add('U');

    String str = "KAKEKIKOKUKaKeKiKoKuK";
    char[] strArray1 = str.toCharArray();
    char[] strArray2 = str.toCharArray();
    int pos1 = -1;
    int pos2 = strArray1.length - 1;
    int size = strArray1.length;
    Stack<Character> vowelsStack = new Stack<>();
    boolean isPos1Vowel = false;

    while (true) {
        if (pos2 > -1) {
            if (vowels.contains(strArray2[pos2])) {
                vowelsStack.add(strArray2[pos2]);
            }
            pos2--;
        }
        if (isPos1Vowel) {
            if (!vowelsStack.isEmpty()) {
                strArray1[pos1] = vowelsStack.remove(0);
                pos1++;
                if (pos1 < size) {
                    isPos1Vowel = vowels.contains(strArray1[pos1]);
                } else {
                    break;
                }
            }
        } else {
            pos1++;
            if (pos1 < size) {
                isPos1Vowel = vowels.contains(strArray1[pos1]);
            } else {
                break;
            }
        }
    }

    System.out.println(new String(strArray1));

请告诉我它是否有效。