Palindrome中的一串字符串

时间:2015-07-25 08:17:43

标签: arrays string algorithm time-complexity palindrome

您将获得一系列字符串。你需要找出总数(a,b),使a和b是回文。示例:html,xml,lmth,css,lmx,xhtml。这里对的总数= 2,因为对(html,lmth)& (xml,lmx)是回文对。蛮力方法是O(n ^ 2)解。

for(int i = 0; i < N; i++)
{           
    for(int j = i + 1; j < N; j++)
    {
        StringBuffer sb = new StringBuffer(a[j]);
        sb = sb.reverse();

        if(a[i].equals(sb.toString()))
            count++;
    }
}

随着N增加,程序变慢。那么找出回文对的最有效方法是什么,以便时间复杂度<1。为O(n ^ 2)。

2 个答案:

答案 0 :(得分:1)

我使用两个包含字符串及其反转形式的集合,如下所示(我将尝试重用您的代码):

Set<String> def = new HashSet<String>();
Set<String> rev = new HashSet<String>();

for(int i = 0; i < N; i++) {
    StringBuffer sb = new StringBuffer(a[i]);
    sb.reverse();
    String srev = sb.toString();

    if(rev.contains(a[i]) || def.contains(srev))
        ++count;

    def.add(a[i]);
    rev.add(srev);
}

复杂性仍会随N而增加,因为您必须至少访问一次每个字符串,但它不再是N的平方。

无论如何,即使下面的代码也应该有用。 我将在几分钟内解释原因。

Set<String> set = new HashSet<String>();

for(int i = 0; i < N; i++) {
    StringBuffer sb = new StringBuffer(a[i]);

    if(set.contains(sb.reverse().toString()))
        ++count;

    set.add(a[i]);
}

这里的想法是,对于每个字符串,检查是否已经访问过反转形式,然后将初始表单放入集合中。

在数组中,您要查找的两个字符串显然位于不同的位置。当您遇到第一个时,其反转形式不在集合中,因此您不会增加计数器。当你遇到第二个时,它的反转形式在集合中,因为它是以前遇到的原始字符串,所以你只增加一次计数器。

这个解决方案有问题,如果你有像这样的数组:[xml,lmx,lmx],但我不知道你在这些情况下的期望(无论如何,你可以使用多集或哈希映射并从中删除元素该集合实现了不同的结果)。

答案 1 :(得分:1)

除了private static int countDichotomic (String[] a) { int count = 0 ; Arrays.sort(a) ; for(int i = 0; i < a.length; i++) { StringBuffer sb = new StringBuffer(a[i]); sb.reverse(); if (Arrays.binarySearch(a, i + 1, a.length, sb.toString()) > i) count ++ ; } return count ; } 回答,并提供另一种算法(和比较),这里是一个二分法搜索:

N = 10000
Original:   2113ms
HashSet:    7ms
Dichotomic: 10ms

N = 100000
Original:   Unknow (too long)
HashSet:    83ms
Dichotomic: 147ms

N = 1000000
Original:   Unknow (too long)
HashSet:    1137ms
Dichotomic: 1951ms

我显然不是Java开发者,所以请原谅我糟糕的编码风格......

以下是3方法的一些结果:

20

重要说明:这些结果适用于长度为HashSet的字符串,该字符串很短。随着字符串长度的增加,DichotomicDichotomic方法之间的差异变得越来越短。在我的计算机上,HashSet方法到达1000的字符串长度大约为N = 100000 L = 20 HashSet: 83ms Dichotomic: 147ms L = 100 HashSet: 115ms Dichotomic: 167ms L = 300 HashSet: 169ms Dichotomic: 238ms L = 500 HashSet: 262ms Dichotomic: 319ms L = 1000 HashSet: 384ms Dichotomic: 386ms L = 5000 HashSet: 1866ms Dichotomic: 895ms ,两者之间的差距随着字符串长度的增加而增加:

long startTime, endTime, count = 0 ;
double duration ;
startTime = System.nanoTime();
count = countMethod (arrayOfStrings);
endTime = System.nanoTime();
duration = (endTime - startTime) / 1000000 ;
System.out.println("Original: " + count + ", " + duration + "ms");

我用来计算时间的代码:

import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class PalindromeSearch {

    private static String random () {
        char[] chars = "abcdefghijklmnopqrstuvwxyz".toCharArray();
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < 20; i++) {
            char c = chars[random.nextInt(chars.length)];
            sb.append(c);
        }
        return sb.toString();
    }

    private static int N_String = 1000000 ;

    private static int countOriginal (String[] a) {
        int count = 0 ;
        for(int i = 0; i < a.length; i++)
        {           
            for(int j = i + 1; j < a.length; j++)
            {
                StringBuffer sb = new StringBuffer(a[j]);
                sb = sb.reverse();

                if(a[i].equals(sb.toString()))
                    count++;
            }
        }
        return count ;
    }

    private static int countHashSet (String[] a) {
        int count = 0 ;
        Set<String> def = new HashSet<String>();
        Set<String> rev = new HashSet<String>();

        for(int i = 0; i < a.length; i++) {
            StringBuffer sb = new StringBuffer(a[i]);
            sb.reverse();
            String srev = sb.toString();

            if(rev.contains(a[i]) || def.contains(srev))
                ++count;

            def.add(a[i]);
            rev.add(srev);
        }
        return count ;
    }

    private static int countDichotomic (String[] a) {
        int count = 0 ;
        Arrays.sort(a) ;

        for(int i = 0; i < a.length; i++) {
            StringBuffer sb = new StringBuffer(a[i]);
            sb.reverse();
            if (Arrays.binarySearch(a, i + 1, a.length, sb.toString()) > i)
                count ++ ;
        }
        return count ;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        String[] arrayOfStrings = new String[N_String] ;
        for (int i = 0 ; i < arrayOfStrings.length ; i++) {
            arrayOfStrings[i] = random () ;
        }

        // arrayOfStrings = new String[] {"html", "xml", "lmth", "css", "lmx", "xhtml"} ;

        long startTime, endTime, count = 0 ;
        double duration ;
        startTime = System.nanoTime();
        // count = countOriginal (arrayOfStrings);
        endTime = System.nanoTime();
        duration = (endTime - startTime) / 1000000 ;  //divide by 1000000 to get milliseconds.
        System.out.println("Original: " + count + ", " + duration + "ms");

        startTime = System.nanoTime();
        count = countHashSet(arrayOfStrings);
        endTime = System.nanoTime();
        duration = (endTime - startTime) / 1000000 ;  //divide by 1000000 to get milliseconds.
        System.out.println("HashSet: " + count + ", " + duration + "ms");

        startTime = System.nanoTime();
        count = countDichotomic(arrayOfStrings);
        endTime = System.nanoTime();
        duration = (endTime - startTime) / 1000000 ;  //divide by 1000000 to get milliseconds.
        System.out.println("Dichotomic: " + count + ", " + duration + "ms");
    }

}

我使用随机字符串,所以我的总计数为0(查看算法,我不认为它会改变结果)。尽管如此,我还不是Java专家,因此可以有更好的方法来对这些功能进行测试。

重要说明:我只计算了1个实例的时间,这实际上并不准确,但对于我获得的结果,2个实例之间没有太多变化(我没有时间写一个非常准确的程序......)。

以下是整个代码:

{{1}}