Leetcode--14.最长公共前缀(改)

按照誓言,已经修改过了,可以放心食用

前引:我打算偶尔也要画个图来讲解我的方法,这样应该可以更好地讲解,但是明天就是端午了,我打算今天这个讲解就偷懒一下,比较敷衍,我感觉我没讲清楚,所以我发誓,端午过后必改,贯彻我的口碑,这次就算了,别跟自己过不去,委屈你了,看我的帖子,晚安

解题思路:

        1.获取信息:

                (1)查找多个字符串的最长公共前缀,即:这些字符串前几个字符相同,返回它们即可

        2.分析问题:(注意,这里的分析思路,我是选取一个字符串作为初始公共前缀,用它来与其他字符串进行比较,求出公共前缀,后续方法,有些不是这个思路,我会说明的,这是我初次看这道题的思路,后面想出来了其他思中)

                看到这个问题,我立马想到的是,字符串之间肯定要进行比较,才能知道它是否有公共前缀,那么就会有两种情况

                注:size1是一个字符串的长度,size2是另一个字符串的长度

                (1)size1==size2

                        直接依次比较两个字符串相对的那一位即可,相等,就继续,不相等,就返回前面相等的几个字符组成的字符串

                (2)size1<size2 || size1>size2

                        不用慌张,因为是求最长公共前缀,那肯定是迁就较短的字符串,那么就会有两种情况

                        1.你初始选取的那个字符串比较短,那你可以照常比较,没啥大差别,一番风顺就进行到最后,不一帆风顺,在比较到末尾之前就返回了

                        2.你初始选取的那个字符串比较长,那你也可以在那个较短的字符串取到末尾下一位时,两者比较相对的位置,肯定是不相同的,所以它自己会返回

        3.示例查验:

                示例2:提醒了我们当不存在公共前缀时,返回 ""

        4.尝试编写代码

                1.横向扫描:

                        (改)思路:我们取字符串数组的第一个字符串作为前缀,再用它与剩下的字符串进行比较,来对前缀进行微调,顺便可以再比较中验证前缀的正确性,比较完最后的一个字符串后,前缀就是最长公共前缀,我给出鲜明的例子,这次配上图

                        我要生动地形容这个方法的话就是,一颗没有修剪过枝叶的树,经过多次修剪,就会成为美丽的树

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        int size=strs.size();//求出有多少个字符串
        if(size==1)return strs[0];//如果只有一个字符串,直接返回
        string prefix=strs[0];//选取字符串数组中的第一个字符串为初始前缀
        for(int i=0;i<size;i++){//遍历字符串数组中的每一个字符串
            if(strs[i]=="")return "";//如果某个字符串为空,直接返回空串
            int size1=strs[i].size();//要比较的那个字符串的长度
            int size2=prefix.size();//前缀的长度(是指的我形容的那个还没有被修剪完枝叶的树)
            int m=0;//比较的位置的下标
            for(int j=0;j<size1;j++){//逐位比较
                if(prefix[m]!=strs[i][j]){//某个位置不等于的话
                    if(m==0&&j==0)return "";//如果一开始就不等于,直接返回空串
                    prefix=strs[i].substr(0,j);//更新前缀,取前面相同的字符组成字符串
                    break;//跳出循环
                }
                if(size2>size1&&j==size1-1){//如果前缀(是指的我形容的那个还没有被修剪完枝叶的树)遇到比它小的字符串
                    prefix=strs[i];//直接更新前缀为更小的那个字符串
                }
                m++;//更新比较位置的下标
            }
        }
        return prefix;
    }
};

                (2)纵向扫描

                                (改)思路:之前我们是修剪树苗,是逐步减少,那么换一种思路,可不可以逐步增多呢?

                                当然可以,我们每次比较字符串数组中所有字符串的同一位置,会有两种情况:

                                1.都相同,那么就加上这个字符,作为前缀

                                2.不是都相同,那么直接返回之前积累的相同的字符组成的前缀

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        string prefix;//初始的前缀为空(这下你该懂得,为什么上一种解法,我要叫修剪树木了吧,这次是积少成多哦)
        int i=0;//比较位置的下标
        while(1){
            if(strs[0]=="")return "";//如果字符串数组中某个字符串为空串,直接返回空串
            if(strs[0].size()<=i)return prefix;//如果某个字符串根本没有i下标的位置(就是到头了,不够长),那直接返回前缀
            char s=strs[0][i];//取得字符串需要比较的那个位置上的字符
            for(auto& str:strs){//依次比较
                if(str[i]!=s){//如果不全等于
                    return prefix;//返回下标
                }
            }
            prefix+=s;//如果全等于,就加上
            i++;//继续比较下一个位置
        }
        return prefix;
    }
};

                (3)分治法

                        分治法:把一个大问题拆分为多个小问题

                        (改)思路:字符串数组,把它划分为二,获得的两个字符串数组,再次划分为二,继续进行上述的划分,直到划分出来的字符串数组里面只有两个字符串,再比较两个字符串,求出它们的前缀,再合各个组的前缀进行比较,最后所得前缀就是答案

                        代码如下:

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        return GetRes(strs,0,strs.size()-1);//我用的是递归哦
    }
private:
    string GetRes(vector<string>& strs,int begin,int end){
        if(end-begin<=1)return Find(strs[begin],strs[end]);//如果划分的某个字符串数组里面只有两个字符串或者一个,直接比较,然后返回即可
        int mid=(begin+end)/2;//求出中点
        string left=GetRes(strs,begin,mid);//求出中点左边的前缀
        string right=GetRes(strs,mid+1,end);//求出中点右边的前缀
        if(left==right)return left;//如果两者相等,直接返回
        return Find(left,right);//如果不相等,比较之后,再返回
    }
    string Find(string& m,string& n){//输入的是两个字符串
        string res;//前缀
        int size1=m.size(),i=0;
        int size2=n.size(),j=0;
        while(i<size1&&j<size2){//依次比较它们相对位置上的字符
            if(m[i]!=n[j])break;//不同直接返回
            res+=m[i];//相同就加上该字符,继续比较下一位置上的字符
            i++;
            j++;
        }
        return res;//返回前缀
    }
};

                (改)(4)二分法(本质上也是,纵向扫描法的优化版本把)(我写到这里太疲惫了,我就说一下思路,等端午节过后,我再补上,也不失为一件美事)

                        (改)思路:我们知道,纵向扫描法,是比较字符串数组中所有字符串相同位置上的字符,如果相同,就加上作为前缀,如不同,就直接返回前面相同的字符构成的前缀

                        那么,我们可以在比较的位置上开始下手优化,让它不用比较那么多位置,不久可以优化了吗?

                        初始比较位置是 i = 0,最后比较的位置自然是某个字符串s它的末尾 j = s.size()了

                        每次求出mid = i + (j - i) / 2 = (i + j) / 2

                        比较下标为mid的那个位置,那么就会有两种情况

                        1.相同,那么最长公共前缀的长度一定大于等于 mid,那么就取,i = mid + 1到 j 这个区间,再次求它们的中点mid来比较这个位置上的字符即可

                        2.不相同,那么最长公共前缀的长度一定小于等于 mid,那么就取,i 到 j = mid 这个区间,再次求它们的中点mid来比较这个位置上的字符即可

                        结束条件:在进行多次比较之后,最后的初始比较位置 i 合最后比较位置 j 肯定是相互趋近,这时,结果也大差不差了

                        以下是代码

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        string prefix=strs[0];//取字符串数组中的第一个字符串为初始前缀
        for(string& str:strs){//取字符串数组中最短的字符串为初始前缀
            if(str==""||prefix[0]!=str[0])return "";
            if(prefix.size()>str.size())prefix=str;
        }
        int begin=0,end=prefix.size()-1;//初始比较位置和最终比较位置
        label://标签
        while(begin<end){//当begin==end时就推出循环
            int mid=(begin+end)/2;//取比较的中点
            for(string& str:strs){//取字符串数组中的字符串
                if(prefix[mid]!=str[mid]){//如果比较的那个位置,前缀和某个字符串不一样
                    end=mid;//取左区间继续进行比较
                    goto label;//前往标签处
                }
            }
            begin=mid+1;//一帆风顺,就取右区间继续进行比较
        }
        if(begin==end&&end==prefix.size()-1){//这一步大有看头,我会在代码栏外面进行讲解
            for(string& str:strs){
                if(str[end]!=prefix[end])return prefix.substr(0,end);
            }
            return prefix;
        }
        return prefix.substr(0,end);
    }
};

我最后一步验证大有看头,是为了专门处理一帆风顺的情况,就是字符串数组中的每个字符串都相同这种特殊的情况,即,可以一帆风顺走到末尾

其他的情况,或多或少都会前往标签处几次,最后

return prefix.substr(0,end);

就会返回合适的长度,但我说的那个特殊情况会少一位,所以我专门处理了一下这个特殊情况

如果你可以处理我这个不足之处,恳求你私信给我,谢谢老师

大概思路就是这个,我这次不写,不是因为我比较懒,想等端午后再补出来,而是我想考考你,磨练一下你,你可以在端午过后来这个帖子对答案嘛

虽然要端午了,但还是要提一嘴,纸上得来终觉浅,绝知此事要躬行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值