[LeetCode][14]Longest Common Prefix解析 两种算法和底层源码的深入对比-Java实现

Q:

Write a function to find the longest common prefix string amongst an array of strings.

A:

这题的大概意思就是说给你一组字符串找出其中最长的哪个通用的前缀出来。这个东西不难找,但是如何找的又快又好不简单。其实这题本来就是easy题,但是却让我联想到了《数据结构与算法分析》上的一道题目,那道题目是这样的:

给一个8900个字的字典,从中间找出类似abc、bbc、abb这样单词中只有一个字母不同的单词进行分类,最差的算法,用的大家都想到的遍历算法进行分类耗时90多秒,更新到第四种算法的时候只用4秒钟了(最后还有一种3秒钟的方法)。他用的最关键的改变就是把单词切出来一个字母然后存在TreeMap里,如果TreeMap有的话就在里面的size上+1,最后找出size大于2的值,当然我之前说过有一种3秒钟的方法就是使用的hashmap。

关于hashmap和treemap的区别我会在我的数据结构与算法分析复习的部分讲。如果有兴趣请关注我的博客。

那我们来想一想我们可不可以用这种方法增加速度呢?

首先我们需要找出前缀的范围,有三种选择:

1、找出最小的字符,遍历每个字母当作前缀。O(n)

2、找出第一个字符,遍历每个字母当作前缀O(1)

3、找出前两个字符的共同部分,遍历字母当作前缀O(min(a.length,b.length).

对于我们得到的前缀范围我们可以得到她们的数量size。

遍历size来分割接下来的字符串,比如说[0,1],[0,2],[0,3]这样O(size*n)

最后找出这么多字符中谁的size==总String数量并且她的值最大就是我们要的最长前缀了O(size)

这样我们最后最好的时间复杂度的情况应该是O(size*n)并且0<size<=n,size为整数,也就是说O(size*n)>O(n)

那么我们来看第二种方法:

首先使用前两个对比,得出通用前缀prefix。O(prefix.length)

使用通用前缀根接下来的遍历,得出跟prefix的通用前缀并赋值给他自己O(n)

最后返回prefix

她的时间复杂度是O(prefix.length*n)

O(prefix.length*n)和O(size*n)谁大谁小真的不好比较。我们来分析一下

如果size的确定使用第三种办法,那么应该是跟第二种方法中的第一次取得prefix相等即O(prefix.length)=O(min(a.length,b.length).

对于第二种方法之后的prefix.length肯定小于第一次的prefix.length。但是对于第一种方法而言第一次的prefix.length等于之后的size。选取第一种里面的1和2的话size肯定是大的。所以经过对比分析第二种方法时间复杂度更小。

所以我们使用第二种方法计算,代码如下:

	public static void main(String[] args){
		String[] strs= {"aa","a"};
		System.out.println(method(strs));
	}

	private static String method(String[] strs) {
		// TODO Auto-generated method stub
		if(strs.length==0)
            return "";
        String prefix =strs[0];
		for(int i = 1;i<strs.length;i++){
			//char thisc = prefix.charAt(0);
			if(prefix.length()>strs[i].length()){
				prefix = prefix.substring(0, strs[i].length());
			}
			for(int j = 0;j< prefix.length();j++){
				if(prefix.charAt(j)!=strs[i].charAt(j)){//自负不相等截取prefix
					prefix = prefix.substring(0, j);
				}
			}
		}
		return prefix;
	}

当然这个是原版代码,后来发现要5ms,我在leetcode上参考了一下,修改成了如下代码:

	public static void main(String[] args){
		String[] strs= {"aa","a"};
		System.out.println(method(strs));
	}

	private static String method(String[] strs) {
		// TODO Auto-generated method stub

		if(strs.length==0)
			return "";
		String prefix =strs[0];
		for(int i = 1;i<strs.length;i++){
			while(strs[i].indexOf(prefix) != 0){
				prefix = prefix.substring(0,prefix.length()-1);

			}
		}
		return prefix;
	}

这样耗时1ms,问题就在一个方法上String.indexOf,为什么这个方法效率那么高呢?

我们看一下源码:

static int indexOf(char[] source, int sourceOffset, int sourceCount,
            char[] target, int targetOffset, int targetCount,
            int fromIndex) {
        if (fromIndex >= sourceCount) {
            return (targetCount == 0 ? sourceCount : -1);
        }
        if (fromIndex < 0) {
            fromIndex = 0;
        }
        if (targetCount == 0) {
            return fromIndex;
        }

        char first = target[targetOffset];
        int max = sourceOffset + (sourceCount - targetCount);

        for (int i = sourceOffset + fromIndex; i <= max; i++) {
            /* Look for first character. */
            if (source[i] != first) {
                while (++i <= max && source[i] != first);
            }

            /* Found first character, now look at the rest of v2 */
            if (i <= max) {
                int j = i + 1;
                int end = j + targetCount - 1;
                for (int k = targetOffset + 1; j < end && source[j]
                        == target[k]; j++, k++);

                if (j == end) {
                    /* Found whole string. */
                    return i - sourceOffset;
                }
            }
        }
        return -1;
    }

默认参数:

value, 0, value.length,str.value, 0,
str.value.length,
0

我们可以看出来这段代码的思路就是遍历到第一个字符开始然后对比,都相同就返回index否则返回-1.

那我们根据这个思路修改一下原本的代码试试。

	if(strs.length==0)
            return "";
        String prefix =strs[0];
		for(int i = 1;i<strs.length;i++){
			//if(prefix.length()>strs[i].length()){
//				prefix = prefix.substring(0, strs[i].length());
			//}
			int max= Math.min(prefix.length(), strs[i].length());
			int j = 0;
			for(;j< max&&prefix.charAt(j)==strs[i].charAt(j);j++){}
			prefix = prefix.substring(0, j);

		}
		return prefix;

最后我把源码改成这样也还有4ms,然后我仔细对比源码发现问题应该出在String.charat()上面了,代码如下:

 public char charAt(int index) {
        if ((index < 0) || (index >= value.length)) {
            throw new StringIndexOutOfBoundsException(index);
        }
        return value[index];
    }

里面有两个判断非常耗费时间,我们改成如下代码

if(strs.length==0)
            return "";
        String prefix =strs[0];
		for(int i = 1;i<strs.length;i++){
			//if(prefix.length()>strs[i].length()){
//				prefix = prefix.substring(0, strs[i].length());
			//}
			char[] str = strs[i].toCharArray();
			int max= Math.min(prefix.length(), str.length);
			int j = 0;
			for(;j< max&&prefix.charAt(j)==str[j];j++){}
			prefix = prefix.substring(0, j);

		}
		return prefix;

就变成了2ms,果然问题出在这里,如果我们不使用tochararray()方法的话就应该是1ms了,这说明我们的思路和算法都没有错,底层还有很多需要注意的东西

时间: 2024-08-27 10:26:25

[LeetCode][14]Longest Common Prefix解析 两种算法和底层源码的深入对比-Java实现的相关文章

leetCode 14. Longest Common Prefix 字符串

14. Longest Common Prefix Write a function to find the longest common prefix string amongst an array of strings. 题目大意:求一组字符串的最长前缀. 代码如下: class Solution { public:     string longestCommonPrefix(vector<string>& strs) {         if(strs.size() == 0)

[LeetCode] 14. Longest Common Prefix 最长共同前缀

Write a function to find the longest common prefix string amongst an array of strings. 这题有好几种解法,个人认为会1,2的解法就可以了,但这种多方法解题的思路可以好好学习一下.具体可参考:Longest Common Prefix 1. 一个一个字符串取比较word by word matching: 先拿前2个,从第一位开始比较,直到发现有不同的字符,此时前面一样的字符串在去和后面的字符串比较,直到结束.可

Java [leetcode 14] Longest Common Prefix

小二好久没有更新博客了,真是罪过,最近在看linux的东西导致进度耽搁了,所以今晚睡觉前怒刷一题! 问题描述: Write a function to find the longest common prefix string amongst an array of strings. 解题思路: 该问题就是找到所有数组字符串里面的最长相同前字串.所以我的思路是先找到数组中最短的那个字符串,然后每次比较的时候最多循环该长度就行,这样避免字符串下标溢出的问题.设置StringBuilder对象用于存

LeetCode 14. Longest Common Prefix

Write a function to find the longest common prefix string amongst an array of strings. 让我们在一个字符串中找出所有字符串的共同前缀 class Solution { public: string longestCommonPrefix(vector<string>& strs) { string result = ""; if (!strs.size())return resul

LeetCode #14 Longest Common Prefix (E)

[Problem] Write a function to find the longest common prefix string amongst an array of strings. [Analysis] 思路非常简单,循环验证每一个字符串就可以通过OJ,代码也没有优化. [Solution] public class Solution { public String longestCommonPrefix(String[] strs) { if (strs.length == 0)

LeetCode 14 Longest Common Prefix (C,C++,Java,Python)

Problem: Write a function to find the longest common prefix string amongst an array of strings. Solution: 时间复杂度O(n) 题目大意: 给一个字符串数组,要找到这些字符串的最大前缀公共子串. 解题思路: 既然是公共子串,那每个字符串肯定都包含有,并且在头部,首先把第一个字符串作为默认最大,然后依次与后边每一个字符串对比,计算所有的最大匹配长度,长度最小的就是 Java源代码(用时263ms

LeetCode 14 Longest Common Prefix 最长前缀

题目:Write a function to find the longest common prefix string amongst an array of strings. 翻译:求一个字符串数组中 共同的最长前缀. 思路:以第一个串为基准,逐个位置遍历,并遍历字符串数组,如果出现某个字符串长度小于当前位置,或者出现当前位置的字符不相同,返回字串strs[0].substring(0,pos):思路很简单. 代码: public String longestCommonPrefix(Str

[LeetCode][Python]14: Longest Common Prefix

# -*- coding: utf8 -*-'''__author__ = '[email protected]'https://oj.leetcode.com/problems/longest-common-prefix/14: Longest Common Prefix Write a function to find the longest common prefix string amongst an array of strings.===Comments by Dabay===注意边

14. Longest Common Prefix【leetcode】

14. Longest Common Prefix Write a function to find the longest common prefix string amongst an array of strings. 寻找一个数组中最长的公共前缀 例如["baaa","caaabbb","aaaa"]输出"aaa" 结题思路: 判断非空的情况在进行计算 取第一个字符串最为标杆进行对比,因为最终结果一定在第一位中 用第一