Leetcode - 最长的通用前缀 - 为什么我的运行时与解决方案相比如此之慢?

时间:2017-07-14 05:18:20

标签: java string runtime time-complexity

问题:编写一个函数来查找字符串数组中最长的公共前缀字符串。

这是一个来自leetcode的简单问题,下面是我的回答VS.解决方案答案。问题是:我的答案胜过运行速度的1.17%,解决方案胜率为79.65%。 为什么我的代码这么慢?

在我们开始操作初始公共字符串之前,我们的代码非常相似。解决方案是通过在String类中调用indexof和substring函数来实现的,并且我使用findCommon函数来完成它,这是由我定义的。

解决方案:

        public String longestCommonPrefix(String[] strs) {
            if(strs == null || strs.length == 0)    return "";
            String pre = strs[0];
            int i = 1;
            while(i < strs.length){
                while(strs[i].indexOf(pre) != 0)
                    pre = pre.substring(0,pre.length()-1);
                    i++;
                }
                return pre;
        }

这是我的:

     public static String longestCommonPrefix(String[] strs){
         if(strs == null || strs.length == 0)
            return "";
         String result = strs[0];
         for(int index = 1; index < strs.length; index++)
            result = findCommon(result, strs[index]);   
         return result;
     }

     public static String findCommon(String a, String b){
         String common = ""; 
         for(int index = 0; index < Math.min(a.length(), b.length()); index++)
         {
             if(a.charAt(index) == b.charAt(index))
                 common += a.charAt(index); 
             else 
                 break; 
         }
         return common; 
      }

在我看来,解决方案代码看起来更简单,因为函数是在String库中定义的。但这并不意味着它们不存在。

2 个答案:

答案 0 :(得分:1)

看看你如何构建前缀字符串:

     String common = ""; 
     for(int index = 0; index < Math.min(a.length(), b.length()); index++)
     {
         if(a.charAt(index) == b.charAt(index))
             common += a.charAt(index); 
         else 
             break; 
     }
     return common;

每次执行

common += a.charAt(index);

Java必须创建一个全新的String对象,该对象是通过在现有字符串common的末尾添加新字符而形成的。这意味着制作长度为p的前缀字符串的成本最终为O(p 2 )。如果你有n个字符串,那么程序的运行时就像O(np 2 )。

将此与参考解决方案进行对比:

pre = pre.substring(0,pre.length()-1);

在许多Java实现中,创建子字符串的行为需要时间O(1),因为新字符串可以与原始字符串共享基础字符数组(一些索引被调整以考虑新的起始索引)。这意味着通过p前缀的工作成本将是O(p)而不是O(p 2 ),这可能导致更长字符串的性能大幅提升。

答案 1 :(得分:0)

我用户试图解决这个问题。您可以尝试使用trie

#define MAX 30    //the total number of alphabet is 26, a...z

struct DicTrie{
    bool isTerminal;//是否是单词结束标志
    int  count;     //当前字符串出现次数
    int  branchCount;    //计数当前节点的孩子数
    struct DicTrie *next[MAX ]; //每个节点 最多 有 MAX 个孩子节点 结构体嵌套
};


int insertTrie(struct DicTrie *root ,char *targetString)
{
    if (!targetString) {
        return 0;
    }
    int len = strlen(targetString);
    if (len <= 0) {
        return 0;
    }
    struct DicTrie *head = root;
    for (int i = 0; i < len; i ++) {
        int res = (int)(targetString[i] - 'a');//当前小写字母对应数字
        if (head->next[res] == NULL) { //如果是空节点
            head->next[res] = (struct DicTrie *)malloc(sizeof(struct DicTrie));//new DicTrie;//则插入新节点元素
            head = head->next[res]; //更新头指针 并初始化
            head->count = 0;        //
            for (int j = 0; j < MAX; j ++) {
                head->next[j] = NULL;
                head->isTerminal = false;
            }
            head->branchCount = 1;//一个分支
        } else {
            head = head->next[res];
            head->branchCount ++;//分支累计
        }
    }
    head->count ++;//每次插入一个,响应计数都增加1
    head->isTerminal = true;
    return head->count;
}

char* longestCommonPrefix(char** strs, int strsSize) {

    int len = strsSize;
    //边界处理
    if (len == 0) {
        return "";
    }
    if (len == 1) {
        return strs[0];
    }
    //组织字典树
    struct DicTrie *root = NULL;
    root = (struct DicTrie *)malloc(sizeof(struct DicTrie));
    root->count = 0;
    root->branchCount = 0;
    for (int  i = 0; i < MAX; i ++) {
        root->next[i] = NULL; // 空节点
        root->isTerminal = false; //
    }
    //
    for (int i = 0;i < len; i ++) {
        insertTrie(root, strs[i]);
    }
    //
    int preIndex = 0;

    struct DicTrie *head = root;
    bool isFlag = false;
    int i = 0;
    int count = strlen(strs[0]);//任意一字符串都可以 从strs[0]中查即可
    for (preIndex = 0; preIndex< count; preIndex ++) {
        int targetIndex = strs[0][preIndex] - 'a';
        head = head->next[targetIndex];
        if (head->branchCount == len) {
            i ++;//拿到合法前缀的计数
            isFlag = true;
        }
    }
    if (isFlag) {
        preIndex = i;
    } else {
        preIndex = 0;
    }
    strs[0][preIndex] = '\0';
    return  strs[0];
}

运行时速度还可以。 enter image description here