LeetCode 14. 最长公共前缀(C++)

题目地址:力扣

解法1:纵向扫描(版本1)

思路:既然要找公共子前缀,那么直接遍历整个数组,首先看一个字母是不是,是的话看两个字母是不,以此类推。要注意的是,公共子前缀的最长长度不超过数组中最短的字符串长度。因此可以先通过一轮遍历找出数组中最短的字符串。再进行正式的比较操作。

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        // 假定最短的字符串是数组中的第一个
        string shortest = strs[0]; 
        // 遍历一遍找出最短的字符串
        for(auto i : strs)
            if (i.size() < shortest.size())
                shortest = i;
        // 若最短的字符串为空则直接返回空
        if (shortest != "")
        {
            // 定义sz为公共子前缀长度,prefix为公共子前缀
            int sz = 1;
            string prefix;
            // 外层循环,直到公共子前缀等于最短字符串
            for (; sz <= shortest.size(); ++sz)
            {
                // 本轮循环中子前缀为最短字符串的前面sz个字符
                prefix = shortest.substr(0, sz);
                // 遍历strs判断是否是公共子前缀
                for (auto i : strs)
                    // 如果发现有一个不满足,那么子前缀一定是当前长度-1的子串
                    if (i.substr(0, sz) != prefix)
                        return shortest.substr(0, sz-1);
            }
            return prefix;
        }
        return "";
    }
};

解法2:纵向扫描(版本2)

思路:对于数组中每一个字符串,依次比较它们的第一位是否一样,是的话就继续检查第二位,直至检查到某个字符串末尾,时间复杂度是O(mn),空间复杂度O(1)

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        // sz代表当前比较的字符位置,currC代表当前比较字符
        int sz = 0;
        char currC;
        /* 循环条件是sz不超过数组中第一个元素的长度,即最大取第一个元素的长度-1
           此时正好对应着下标取到第一个元素的最后一个字符
        */
        while (sz < strs[0].size())
        {
            // currC设置为当前想要比较的字符
            currC = strs[0][sz];
            // 循环比较字符串中每个元素的当前字符是否都与currC相等
            for(auto i : strs)
            {
                // 若出现有元素的长度小于sz或者当前比较的地方不相等
                if (i.size() < sz || i[sz] != currC)
                    // sz只要大于0,就返回第一个元素从0开始,长度为sz的字符串,若sz=0则返回空串
                    return sz > 0 ? strs[0].substr(0, sz) : "";
            }
            // 每轮检查无异常就递增sz
            ++sz;
        }
        // 若出现sz大于等于第一个元素的长度,那么直接返回第一个元素
        return strs[0];
    }
};

解法3:横向扫描

这种方法只需要扫描一遍数组,那么时间复杂度就是O(mn),而且空间复杂度O(1)

思路:先比较数组中的第一个元素和第二个,挑选长度较小的那个从后往前比,可以找到最大的匹配位置max_match(即最长相等处的下标)。依次类推,当max_match等于-1的时候直接返回空串。否则遍历完成后,返回第一个元素的substr(0, max_match+1)

class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        // 初始化当前扫描的下标以及最大匹配处下标为第一个元素的长度-1
        int idx = strs[0].size() - 1;
        int max_match = strs[0].size() - 1;
        // 从第二个元素开始遍历
        for (int i = 1; i < strs.size() ; ++i)
        {
            // 开始扫描的位置需要取max_match和当前元素长度-1二者之间较小的那个
            idx = (strs[i].size() - 1 < max_match) ? strs[i].size() - 1 : max_match;
            // 若idx有更新,同时更新max_match
            max_match = idx;
            // 从后往前扫描,直到扫描到字符串的第一个字符为止
            for (; idx >= 0; --idx)
            {
                // 
                if (strs[i-1][idx] != strs[i][idx])
                    max_match = idx - 1;
            }
            if (max_match == -1)
                return "";
        }
        // 若没有第二元素则直接返回第一个元素;若有的话返回最长公共前缀
        return strs[0].substr(0, max_match + 1);
    }
};

解法4:分治法

思路:就是将大问题分解为子问题,要求整个数组的,就可以把数组分成左右两部分,分别求两部分的公共子前缀,再合起来看一下哪个才是总的公共子前缀。以此类推

class Solution {
public:    
    string divide(vector<string>& strs, int start, int end)
    {
        // 若头尾相等,则直接返回当前元素
        if (start == end)
            return strs[start];
        // 若头尾相差1,则求两个字符串的公共子前缀并返回
        if (end - 1 == start)
        {
            int i = 0;
            for (; i < min(strs[start].size(), strs[end].size()); ++i)
                if (strs[start][i] != strs[end][i])
                    return  i == 0 ? "" : strs[start].substr(0, i);
            return strs[start].substr(0, i);
        }
        // 若头尾相差的不止1,则分治求前半部分的和后半部分的
        string pre_1 = divide(strs, start, (start+end) / 2);
        string pre_2 = divide(strs, (start+end) / 2 + 1, end);

        // 若前半部分或后半部分为空串,则直接返回空
        if (pre_1 == "" || pre_2 == "")
            return "";

        // 找到前半部分与后半部分前缀的共同部分
        int idx = 0;
        for (; idx < min(pre_1.size(), pre_2.size()); ++idx)
            if (pre_1[idx] != pre_2[idx])
                break;
        return idx == 0 ? "" :pre_1.substr(0, idx);
    }

    string longestCommonPrefix(vector<string>& strs) {
        return divide(strs, 0, strs.size()-1);
    }
};

Accepted

  • 124/124 cases passed (0 ms)
  • Your runtime beats 100 % of cpp submissions
  • Your memory usage beats 8.48 % of cpp submissions (10.1 MB)
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值