1 算法题 :找到字符串中最长的公共前缀
1.1 题目含义
找到字符串中最长的公共前缀是一个常见的字符串处理问题。给定一个字符串数组,需要找到数组中所有字符串的最长公共前缀。最长公共前缀是指所有给定字符串共同拥有的前缀子串,并且这个子串是所有公共前缀中最长的。如果没有公共前缀,则返回空字符串。
1.2 示例
示例 1:
输入: [“flower”,“flow”,“flight”]
输出: “fl”
解释: 这三个字符串的公共前缀是 “fl”,因此返回 “fl”。
示例 2:
输入: [“dog”,“racecar”,“car”]
输出: “”
解释:这三个字符串没有公共前缀,因此返回空字符串。
示例 3:
输入: [“a”]
输出: “a”
解释: 数组中只有一个字符串,该字符串本身即为公共前缀,因此返回 “a”。
2 解题思路
解题思路如下:
(1)特殊情况处理: 首先检查输入字符串数组是否为空。如果为空,那么自然没有公共前缀,直接返回空字符串。
(2)初始化公共前缀: 将数组中的第一个字符串作为初始的公共前缀。这是因为,如果数组中有公共前缀,那么它必然也是第一个字符串的前缀。
(3)遍历字符串数组: 从数组的第二个字符串开始,逐个与当前的公共前缀进行比较。
(4)比较当前字符串与公共前缀: 使用字符串的查找函数(如 find)来确定当前字符串是否以公共前缀开头。如果不是,说明公共前缀需要缩短。
(5)缩短公共前缀: 如果当前字符串不以公共前缀开头,那么我们需要缩短公共前缀。这可以通过截取公共前缀的最后一个字符来实现,然后再次检查当前字符串是否以新的公共前缀开头。这个过程一直持续到找到真正的公共前缀,或者公共前缀被缩短为空字符串为止。
(6)返回结果: 如果在遍历完所有字符串后,公共前缀仍然非空,那么它就是最长的公共前缀,直接返回即可。如果在遍历过程中公共前缀被缩短为空字符串,那么说明没有公共前缀,返回空字符串。
这个算法的关键在于利用字符串的查找函数来快速确定一个字符串是否以另一个字符串开头,并通过不断缩短公共前缀来找到最长的公共前缀。该算法的时间复杂度与字符串数组的大小和最短字符串的长度有关,因为最坏情况下,每个字符串都需要与其进行比较并可能需要多次缩短公共前缀。
3 算法实现代码
3.1 使用遍历缩短公共前缀
如下为算法实现代码:
#include <string>
#include <iostream>
#include <vector>
class Solution
{
public:
std::string longestCommonPrefix(std::vector<std::string>& strs) {
if (strs.empty()) {
return "";
}
// 获取第一个字符串作为初始的公共前缀
std::string prefix = strs[0];
// 遍历字符串数组,从第二个字符串开始比较
for (size_t i = 1; i < strs.size(); ++i) {
// 当前字符串如果不以公共前缀开头,或者当前字符串比公共前缀短
while (strs[i].find(prefix) != 0 || strs[i].size() < prefix.size()) {
// 缩短公共前缀,去掉最后一个字符
prefix = prefix.substr(0, prefix.size() - 1);
// 如果公共前缀已经为空,则没有公共前缀
if (prefix.empty()) {
return "";
}
}
}
// 返回最长的公共前缀
return prefix;
}
};
这段代码首先检查字符串数组是否为空,然后初始化公共前缀为第一个字符串。之后,它遍历数组中的每一个字符串,并与当前的公共前缀进行比较。如果某个字符串不是以公共前缀开头,或者它的长度小于公共前缀的长度(这意味着它无法包含整个公共前缀),则开始缩短公共前缀,直到找到一个所有字符串都共享的公共前缀,或者公共前缀被缩短为空字符串为止。最后,返回找到的最长公共前缀。
调用上面的算法,并得到输出:
int main()
{
Solution s;
// 示例测试
std::vector<std::string> strs1 = { "flower", "flow", "flight" };
std::cout << s.longestCommonPrefix(strs1) << std::endl; // 输出: fl
std::vector<std::string> strs2 = { "dog", "racecar", "car" };
std::cout << s.longestCommonPrefix(strs2) << std::endl; // 输出:
std::vector<std::string> strs3 = { "a" };
std::cout << s.longestCommonPrefix(strs3) << std::endl; // 输出: a
std::vector<std::string> strs4 = {};
std::cout << s.longestCommonPrefix(strs4) << std::endl; // 输出:
return 0;
}
上面代码的输出为:
fl
a
3.2 使用纵向扫描法
纵向扫描法通过比较所有字符串在同一位置的字符是否相同,从字符串的第一个字符开始逐列检查,直到找到不同的字符或者遍历完所有字符串。
以下是基于纵向扫描法的码实现:
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
class Solution
{
public:
std::string longestCommonPrefix(std::vector<std::string>& strs) {
if (strs.empty()) {
return "";
}
// 获取最短字符串的长度,因为公共前缀的长度不会超过最短字符串的长度
int minLength = strs[0].size();
for (const std::string& str : strs) {
minLength = std::min(minLength, static_cast<int>(str.size()));
}
// 纵向扫描,比较同一位置的字符
for (int i = 0; i < minLength; ++i) {
// 假设第一个字符串的当前字符是公共前缀的一部分
char currentChar = strs[0][i];
// 检查其他字符串的当前位置字符是否相同
for (size_t j = 1; j < strs.size(); ++j) {
// 如果当前位置字符不相同,或者已经遍历完某个字符串,则没有公共前缀
if (strs[j].size() <= i || strs[j][i] != currentChar) {
return strs[0].substr(0, i);
}
}
}
// 如果所有字符都相同,则返回最短字符串(因为公共前缀不会超过最短字符串)
return strs[0].substr(0, minLength);
}
};
这种纵向扫描的方法首先确定了最短字符串的长度,因为公共前缀的长度不会超过这个长度。然后,从字符串的第一个字符开始,依次比较每个字符串在同一位置的字符是否相同。如果所有字符串在同一位置的字符都相同,则继续比较下一个位置的字符;如果某个位置的字符不相同,或者某个字符串已经遍历完,则立即返回当前已经找到的公共前缀。如果所有字符都相同,则返回最短字符串作为公共前缀(因为公共前缀不会超过最短字符串的长度)。
这种方法相比于水平扫描法,在某些情况下可能更加高效,因为它避免了不必要的字符串截取操作,特别是在字符串很长且公共前缀很短的情况下。
4 测试用例
以下是针对上面算法的测试用例,基本覆盖了各种情况:
(1)基础测试用例
输入:[“flower”, “flow”, “flight”]
输出:“fl”
说明:所有字符串的最长公共前缀是 “fl”。
(2)所有字符串相同的情况
输入:[“abc”, “abc”, “abc”]
输出:“abc”
说明:所有字符串都相同,因此整个字符串 “abc” 是公共前缀。
(3)没有公共前缀的情况
输入:[“dog”, “racecar”, “car”]
输出:“”
说明:这三个字符串没有公共前缀。
(4)空字符串的情况
输入:[“”, “to”, “tea”, “tell”]
输出:“”
说明:当输入数组中包含空字符串时,没有公共前缀。
(5)一个字符串为空的情况
输入:[“abc”, “”]
输出:“”
说明:当输入数组中有一个字符串为空时,没有公共前缀。
(6)字符串数组为空的情况
输入:[]
输出:“”
说明:当输入数组为空时,没有公共前缀。
(7)字符串数组只有一个元素的情况
输入:[“abc”]
输出:“abc”
说明:当输入数组只有一个字符串时,该字符串就是公共前缀。
(8)字符串数组中有很长字符串的情况
输入:[“abcdefgh”, “abcdefg”, “abcde”]
输出:“abcde”
说明:尽管有些字符串很长,但公共前缀只到 “abcde”。
(9)包含特殊字符的情况
输入:[“1&23”,“1&2”, “1&&”]输出:“1&”
说明:公共前缀可以包含特殊字符。
(10)包含大小写字母的情况
输入:[“HeLlo”, “Hell”, “Help”]
输出:“He”
说明:公共前缀应忽略大小写差异,但本例中由于大小写不匹配,所以公共前缀只到 “He”。