杂记杂记,顾名思义,就是把在我个人做题中用到的string相关知识记录一下。我比较笨只能用这种方法,不然记不住。
相关题目
思路
其实涉及到了多个字符串比较,就有两种比较方式了,一个是横向比较,一个是纵向比较
1️⃣横向比较
也就是首先对比两条字符串,找到公共部分作为待返回结果res,之后将res与剩下的字符串比较,每次比较刷新res的内容,直到res为空或者比较结束
string Bijiao(const string& str1, const string& str2)
{
int length = min(str1.size(), str2.size());
int index = 0;
while(index < length && str1[index] == str2[index]){
index++;
}
return str1.substr(0,index);
}
以上就是比较函数,通过调用可以得到两个字符串之间的公共前缀
用到的函数:substr(pos, len),从位置pos开始拷贝长度为len的字符
小总结:时间复杂度O(mn),其中m是字符串数组中的字符串的平均长度,n是字符串的数量,空间复杂度是O(1),使用的额外空间存放结果res
2️⃣纵向比较
纵向比较,也就是所有的字符串同时从头开始比较,直到找到不匹配项停止循环
int length = strs[0].size();
int count = strs.size();
for(int i = 0; i < length; i++){
char bj = strs[0][i];
for(int j = 1; j < count; j++){
if(i == strs[j].size() || strs[j][i] != bj){
cout << strs[0].substr(0, i);
}
}
}
cout << strs[0];
这个判断方式看起来有点含糊,一样望去,没有考虑strs[0]和其他字符串谁长谁短的问题,同时如果两个循环结束返回的是strs[0],不过实际情况是,如果真的能够完整的结束这两个循环,也就是表明strs[0]是符合唯一条件的,其他的比如strs[j]比strs[0]短,出现不匹配的字符都会直接返回
小总结:时间复杂度O(mn),其中m是字符串数组中的字符串的平均长度,n是字符串的数量,空间复杂度是O(1),使用的额外空间存放结果res
3️⃣分治
对字符串组使用二分算法,将这一组字符串按照32->16->8->4->2规则(不知道怎么解释分治策略,能看懂就行)分组,最后两两比较得出小组的公共前缀,之后比较所有公共前缀得到结果就是所有字符串的共同共同相同前缀了
string lcp(vector<string>& strs)
{
if(!strs.size()) return "";
else return lcpmid(strs, 0, strs.size() - 1);
}
string lcpmid(const vector<string>& strs, int start, int end)
{
if(start == end) return strs[start];
int mid = (start + end) / 2;
string lcpleft = lcpmid(strs, start, mid);
string lcpright = lcpmid(strs, mid + 1, end);
return Bijiao(lcpleft, lcpright);
}
比较函数就是用的之前纵向比较写的Bijiao(),可以看出核心还是没有变,变的是比较的顺序,这个题感觉是少有的直接比较比二分和分治快的类型,因为只是简单的一重循环,不过要这种思想
小总结:时间复杂度是O(mn),由递推式T(n) = 2*T(n/2) + O(m)而来,m是字符串的平均长度,n是字符串数量。空间复杂度O(mlogn),因为涉及到递推,层数最大为logn
4️⃣对单个字符串进行的二分
核心思想是,最长公共前缀的长度必不可能比字符串数组中最短的那个字符串长度长,这样就限制了长度的范围。获得字符串数组中最短的那个字符串长度之后,在这个范围内进行二分处理,确定公共前缀的长度
bool islcp(const vector<string>& strs, int length)
{
string str0 = strs[0].substr(0, length);
for(int i = 1; i < strs.size(); i++){
for(int j = 0; j < strs[i].size(); j++){
if(str0[j] != strs[i][j]) return false;
}
}
return true;
}
if(!strs.size()) cout << "" << endl;
int minlength = strs[0].size();
for(int i = 1; i < strs.size(); i++){
if(minlength < strs[i].size()) minlength = strs[i].size();
}
int low = 0, high = minlength;
while(low < high){
int mid = (low + high) / 2;
if(islcp(strs, mid)) low = mid;
else high = mid - 1;
}
cout << strs[0].substr(0, low);
当得到公共前缀的长度之后,直接使用substr()拷贝的字符串就是需要的结果
💢这里有一个需要记录的地方,关于取中间值mid
int mid = (high - low + 1) / 2 + low; //官方给出的代码是
int mid = (high + low) / 2; //我写的
但我这种写法在力扣中提交有一个会超时,按官方来就能过,不知道为什么,如果大佬看到了请告诉我( •̀ ω •́ )✧
小总结:时间复杂度O(mnlogm),二分查找的迭代次数执行数是O(logm),每次迭代是需要比较mn个字符,m是字符串数组中字符串的最小长度,n是字符串的数量。空间复杂度是O(1)只记录了长度。吐槽一下,后面方法3和方法4属于看个乐的级别,对于这个简单级别的题目属实是大炮打蚊子