题目地址:https://leetcode-cn.com/problems/longest-common-prefix/java
编写一个函数来查找字符串数组中的最长公共前缀。算法
若是不存在公共前缀,返回空字符串 ""。数组
示例 1:数据结构
输入: ["flower","flow","flight"]
输出: "fl"函数
示例 2:优化
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。code
说明:递归
全部输入只包含小写字母 a-z 。索引
去找到多个串的公共前缀不知道,但咱们至少知道找两个串的公共前缀。因而两两一组用上次公共串找下公共直到n-1次迭代完成最终公共前缀,那么像第一个示例三个串,就须要2次迭代leetcode
也就是说咱们用一个公共前缀与下一个获得公共前缀而后更新覆盖,开始下一次迭代。那么一开始咱们指定strs[0]为第一代的公共前缀,下面是初次提交成功的代码:
public String longestCommonPrefix(String[] strs){ int n = strs.length; if(strs.length == 0) return ""; //最初公共前缀 String common = strs[0]; //两两一次共n-1次迭代 for(int i = 1; i < n; i++){ //一次迭代的过程 int j = 0; char[] cur = common.toCharArray(); char[] next = strs[i].toCharArray(); //strs数组与两个字符串边界 if(next.length == 0) return ""; while( j < cur.length && j < next.length ){ // 扫描到不一样了,那么这个索引在最后一个相同的后面一个 if(cur[j] != next[j]){ common = common.substring(0,j);//注意区间左闭右开 break; // 没有扫描到不一样但但已经结尾了,那么索引就是相同的最后一个 }else if(j == cur.length - 1 || j == next.length - 1){ common = common.substring(0,j+1); break; } j++; } } return common; }
但这段代码是存在问题的,咱们要考虑的问题是在循环里面的操做能不能减小,虽然时间复杂度不会变化。但减小循环体的操做也能够成倍的提高,观察代码首次迭代多少次就一个for循环没有什么问题,那么重点关注的是一次迭代这个过程的代码。咱们这段代码拿出来:
while( j < cur.length && j < next.length ){ // 扫描到不一样了,那么这个索引在最后一个相同的后面一个 if(cur[j] != next[j]){ common = common.substring(0,j);//注意区间左闭右开 break; // 没有扫描到不一样但但已经结尾了,那么索引就是相同的最后一个 }else if(j == cur.length - 1 || i == next.length - 1){ common = common.substring(0,i+1); break; } j++; }
先看循环体,由于两段if都有break,那么有三段只能走一个。咱们想想两段if的操做是否是能够合并的。聪明的小伙伴应该能很快反应过来,无非是有木有扫描超过致使是截取[ 0 , j )和[ 0 , j+1 ),那么其实只要不知足两个相等就出循环那么索引都是在最后相等后加了1,根本不用判断截取[ 0 , j )便可。代码马上能够减小一部分
while( j < cur.length && j < next.length && cur[j] == next[j]){ j++; } common = common.substring(0,j);//注意区间左闭右开
同时它也减小了咱们在循环里的操做。若是循环是n次,那么里面有2-3次操做那么次数就是2-3n,尽可能去减小里层循环的操做次数。除了循环体以外while条件也是随着循环被执行由于是短路与
可能执行一个结束也可能执行三个式子,因此咱们能够减小一次
int length = cur.length > next.length ? next.length : cur.length; while( j < length && cur[j] == next[j]){ j++; } common = common.substring(0,j);//注意区间左闭右开
这就至关于咱们往外层添加了一次操做但减小了循环的一次操做,这些其实优化都不大可是在不换解题思惟以及数据结构等等咱们能够去考虑的优化点
public String longestCommonPrefix(String[] strs){ int n = strs.length; if(strs.length == 0) return ""; //最初公共前缀 String common = strs[0]; //两两一次共n-1次迭代 for(int i = 1; i < n; i++){ //一次迭代的过程 int j = 0; char[] cur = common.toCharArray(); char[] next = strs[i].toCharArray(); //strs数组与两个字符串边界 if(next.length == 0) return ""; int length = cur.length > next.length ? next.length : cur.length; while( j < length && cur[j] == next[j]){ j++; } common = common.substring(0,j);//注意区间左闭右开 } return common; }
其实最开始我想到的解法就是纵向比较同时扫描多个串每一个串的字符都相等再同时日后扫描直到有第一个不一样就终止。
但当时只有一个直观的想法想着一字排开比较相等而后感受处理不了就想到横向的比较好写
int i = 0; while( i < min(length) && str1[i] == str2[i]==..... ){ i++; }
其实这里取一个字符串遍历,在一次遍历里面遍历数组的其余的字符串都进行比较便可
public String longestCommonPrefix(String[] strs) { //为空返回"" if (strs.length == 0) return ""; //取数组减小charAt在循环体 char[] p = str[0].toCharArray(); int n = p.length; for (int i = 0; i < n; i++) { char c = p[i]; //依次判断直到不等出现,截取返回 for (int j = 1; j < strs.length; j++) { if (i == strs[j].length() || strs[j].charAt(i) != c) { return strs[0].substring(0, i); } } } return strs[0]; }
咱们出了经过循环扫描比较获得两串的公共前缀,还能够经过前缀是否包含能够用a.startsWith(b)判断是否以起始索引包含另外一个串,或者用a.indexOf(b) == 0 来判断,没有则删减一位直到找到公共前缀开始下个迭代和解一是同样的只是找公共前缀的方式不一样
public String longestCommonPrefix(String[] strs) { //为空返回"" if (strs.length == 0) return ""; String common = strs[0]; for (String str : strs) { // 若common已经减为"",则说明无公共前缀,直接返回 if (common == "") return common; // 若common在当前str中匹配不上,则减小字符串common的长度,再次尝试匹配 while (!str.startsWith(common)) { common = common.substring(0,common.length() - 1); } } return common; }
整体而言的话其实时间复杂度都是比较高的,毕竟都使用了substring等方法超过n的三次方都差很少因此没有较优解。也是和上一题差很少两题都是体会迭代的一个过程所以也均可以转成递归的形式,那就还能够分治分到最后都是两两一组而后求解自底向上以后会介绍,到此LeetCode初级算法合集中的字符串系列所有完成,开启下个篇章链表。