序号 | 题目 | 难度 | 链接 | 外链 |
---|---|---|---|---|
1. | 仅仅反转字母 | 简单 | 跳转 | LeetCode |
2. | 第一个唯一的字符 | 简单 | 跳转 | LeetCode |
3. | 最后一个单词的长度 | 简单 | 跳转 | LeetCode |
4. | 验证回文串 | 简单 | 跳转 | LeetCode |
5. | 字符串加法 | 简单 | 跳转 | LeetCode |
6. | 字符串乘法 | 中等 | 跳转 | LeetCode |
7. | 字符串转换为整数 | 简单 | 跳转 | 牛客 |
8. | 字符串区间反转 | 简单 | 跳转 | LeetCode |
9. | 反转单词 | 简单 | 跳转 | LeetCode |
10. | 罗马数字转换为整数 | 简单 | 跳转 | LeetCode |
11. | 字符串匹配问题(KMP) | 中等 | 跳转 | LeetCode |
12. | 外观数列 | 简单 | 跳转 | LeetCode |
13. | 二进制加法 | 简单 | 跳转 | LeetCode |
14. | 赎金信 | 简单 | 跳转 | LeetCode |
15. | 求单词的个数 | 简单 | 跳转 | LeetCode |
16. | 重复的子字符串 | 中等 | 跳转 | LeetCode |
17. | URL化 | 简单 | 跳转 | LeetCode |
18. | 学生出勤记录I | 简单 | 跳转 | LeetCode |
19. | 长按键入 | 简单 | 跳转 | LeetCode |
20. | 亲密字符串 | 中等 | 跳转 | LeetCode |
LeetCode917:仅仅反转字母
思路很简单,前后扫描即可
class Solution {
public:
bool Isalpha(char ch)
{
if(ch>='a' && ch<='z')
return true;
if(ch>='A' && ch<='Z')
return true;
return false;
}
string reverseOnlyLetters(string S)
{
size_t begin=0;
size_t end=S.size()-1;
while(begin<end)
{
if(S.empty())
return S;
while(begin<end && !Isalpha(S[begin]))
begin++;
while(begin<end && !Isalpha(S[end]))
end--;
swap(S[begin],S[end]);
begin++;
end--;
}
return S;
}
};
LeetCode387:字符串中第一个唯一字符
这种题暴力破解会导致时间复杂度非常高,所以可以使用散列表的思想,为每个字母添加一个映射,利用数组,相同字母个数就增加。这里可以以字母a作为基准,这样的话数组下标就可以从0开始了
class Solution {
public:
int firstUniqChar(string s)
{
int cout[26]={0};//26个字母进行映射
for(int i=0;i<s.size();i++)//相同字母进行统计
{
cout[s[i]-'a']++;
}
for(int i=0;i<s.size();i++)
{
if(cout[s[i]-'a']==1)//第一个出现1次的输出
return i;
}
return -1;
}
};
牛客:最后一个单词的长度
这道题思路比较简单,但是要注意一个问题,字符串是以空格分隔的,所以使用cin接受时可能导致空格之后的单词无法接收到,所以要使用getline
,他可以接受一行,也就是接受空格
#include <string>
#include <iostream>
using namespace std;
int main()
{
string s;
getline(cin,s);
size_t pos=s.rfind(" ");
if(pos!=string::npos)
{
cout<<s.size()-1-pos<<endl;
}
else
{
cout<<s.size()<<endl;
}
}
LeetCode125:验证回文串
此题也是比较简单
class Solution {
public:
bool isPalindrome(string s)
{
for(int i=0;i<s.size();i++)//将所有的小写字母转换为大写字母
{
if(s[i]>='a' && s[i]<='z')
{
s[i]-=32;
}
}
int begin=0;
int end=s.size()-1;
while(begin<end)
{
while(begin<end && !isalnum(s[begin]))//不是字母或数字
begin++;
while(begin<end && !isalnum(s[end]))
end--;
if(s[begin]!=s[end])
{
return false;
}
else
{
begin++;
end--;
}
}
return true;
}
};
LeetCode415:大数运算之加法(重要)
这是一道非常经典的题目,运算时每次单个位相加,同时注意数字和字符之间的转换,然后注意进位。最后字符由于是尾插进去的,所以要反转
class Solution {
public:
string addStrings(string num1, string num2)
{
string ret;//返回结果字符串
int end1=num1.size()-1;
int end2=num2.size()-1;//end1和end2分别用来从后向前扫描num1和num2字符串
int next=0;//标记进位
while(end1>=0 || end2>=0)//扫描
{
int ret1=0,ret2=0;//用来接受对应单个位置的字符
if(end1>=0)
{
ret1=num1[end1--]-'0';//转换为数字
}
if(end2>=0)
{
ret2=num2[end2--]-'0';//转换为数字
}
int retch=ret1+ret2+next;//单位相加注意加进位
if(retch>=10)//如果大于10表示,下一位要进位
{
retch-=10;//留下个位数字
next=1;
}
else
{
next=0;//如果不大于10,就不仅为
}
ret+=(retch+'0');//转化为字符,并尾插到返回字符数组中
}
if(next==1)//有一种特殊情况就是9999+1,所以特殊处理
{
ret+='1';
}
reverse(ret.begin(),ret.end());//由于是尾插所以注意反转
return ret;
}
};
LeetCode43:大数运算之乘法
解法就是竖式计算,第二行的从后向前每一位去乘第一行的每一位,注意第二行不从倒数第一个开始时要补位
string multiply(string num1, string num2)
{
if(num1=="0" || num2=="0")//0乘任何数都是0
return "0";
string ret="0";//返回的字符串
int n1=num1.size()-1,n2=num2.size()-1;//n1和n2分别扫描num1和num2
for(int i=n2;i>=0;i--)//扫描num2
{
string curr;//单个位相乘,暂时保存的字符串
int add=0;//用于进位
for(int j=n2;j>i;j--)//用于补位
{
curr.push_back('0');
}
int y=num2[i]-'0';//乘法运算,用num2的这一位去乘num1的每一位
for(int j=n1;j>=0;j--)//num1的每一位
{
int x=num1[j]-'0';//转换为数字
int retch=x*y+add;//注意加上进位
curr.push_back((retch%10)+'0');//结果尾插进去字符串
add=retch/10;//进位值
}
while(add!=0)//比如9×9=81,这种特殊情况
{
curr.push_back(add%10+'0');
add/=10;
}
reverse(curr.begin(),curr.end());//由于是尾插,注意反转
ret=addStrings(ret,curr);//结果依次进行字符串相加
}
return ret;
牛客:字符串转换为整数
此题也非常简单
class Solution {
public:
int StrToInt(string str)
{
int flag=1;//判断是否为负数
int begin=0;//起始位置
int end=str.size()-1;//结束位置
int sum=0;//返回的数字
int j=10;//倍加
if(str[begin]=='-')//处理负号
{
flag=-1;
begin++;
}
if(str[begin]=='+')//处理正号
{
flag=1;
begin++;
}
while(begin<=end)
{
if(str[begin]>='0' && str[begin]<='9')//只有合法才可以转换
{
int num=str[begin]-'0';
sum=sum*j+num;
begin++;
}
else
return 0;
}
return sum*flag;
}
};
LeetCode 541:字符串部分区间反转
这道题,用for循环每次控制2k个区间,然后每次循环时去判断剩余字符个数是否小于个,如果大于等于k个就反转这个2k区间的前k个,循环结束时,肯定剩余的字符就小于了k个,然后把剩余字符反转
class Solution {
public:
string reverseStr(string s, int k)
{
for(int i=0;i<s.size();i+=(2*k))
{
if(i+k<=s.size())//剩余的字符大于等于k个,但小于2k
{
reverse(s.begin()+i,s.begin()+i+k);//反转前k个
continue;//继续反转,直到
}
reverse(s.begin()+i,s.begin()+s.size());//剩余k个
}
return s;
}
};
LeetCode557:反转字符串中的单词
这道题可以用到上面讲到过过的find_first_of
,不断找下一个空格
class Solution {
public:
string reverseWords(string s)
{
int begin=0;
int end=s.find_first_of(" ");//先找到第一个空格
if(end==string::npos)//如果整个字符串没有空格
{
reverse(s.begin(),s.end());//翻转整个字符串
}
while(end!=string::npos)//不断找空格
{
reverse(s.begin()+begin,s.begin()+end);//翻转区间
begin=end+1;//begin向后走
end=s.find_first_of(" ",begin);//end从下一个位置开始再找空格
if(end==string::npos)//一旦end=npos时,就无法进入while了,直接把剩余部分翻转
{
reverse(s.begin()+begin,s.end());
break;
}
}
return s;
}
};
罗马数字转换为整数
此题也是非常简单,扫描字符串,挨个累加即可
处理情况时,举个例子,IV应该被解析为4而不是6,所以在case V中,看一下它前一个字符是不是I,如果是I那么它已经被解析为了6,所以而与实际情况相比,相差2,所以减去2即可。依次类推
class Solution {
public:
int romanToInt(string s)
{
int num=0;
for(int i=0;i<s.size();i++)
{
switch (s[i])
{
case 'I':
num+=1;
break;
case 'V':
num+=5;
if(i>0 && s[i-1]=='I')//特殊处理IV
num-=2;
break;
case 'X':
num+=10;
if(i>0 &&s[i-1]=='I')//特殊处理IX
num-=2;
break;
case 'L':
num+=50;
if(i>0 &&s[i-1]=='X')//特殊处理XL
num-=20;
break;
case 'C':
num+=100;
if(i>0 &&s[i-1]=='X')//特殊处理XC
num-=20;
break;
case 'D':
num+=500;
if(i>0 &&s[i-1]=='C')//特殊处理CD
num-=200;
break;
case 'M':
num+=1000;
if(i>0 &&s[i-1]=='C')//特殊处理CM
num-=200;
break;
default:
break;
}
}
return num;
}
};
LeetCode28:字符串匹配问题
此题可以用KMP算法求解,KMP算法对于初学者来说可以说是一个劝退算法,本人在这篇博客中对KMP有很详细的讲解,保证你看完就会
class Solution {
public:
void getnext(string& target,int next[])//next数组
{
int i=0;
int k=-1;
next[0]=-1;
while(i<target.size()-1)//注意越界问题,
{
if(k==-1 || target[i]==target[k])
{
i++;
k++;
next[i]=k;
}
else
{
k=next[k];
}
}
}
int strStr(string haystack, string needle)
{
if(needle=="")//目标串为空,返回0
return 0;
if(haystack=="")//主串为空,返回-1
{
return -1;
}
int* next=new int[needle.size()];//申请next数组
int i=0;//i和j分别扫描主串和目标串
int j=0;
getnext(needle,next);//构建next疏忽组
int m=haystack.size();//两个字符串的长度
int n=needle.size();
while(i<m && j<n)//KMP算法
{
if(j==-1|| haystack[i]==needle[j])
{
i++;
j++;
}
else
{
j=next[j];
}
}
if(j>=needle.size())
return i-needle.size();//返回目标串的下标
else
return -1;//找不到返回-1
}
};
LeetCode38:外观数列
此题题目较长,但是仔细理解后会发现比较有意思。这个数列每个对象都是对前一个对象的描述,所以它要第几个对象,我就剩生成几个对象,就是两个for循环的迭代操作
class Solution {
public:
string countAndSay(int n)
{
string arr[n];//给几个n就生成几个对象
arr[0]="1";//第一个是原始
for(int i=1;i<n;i++)//每一个对象都要对前一个对象进行描述
{
int index=1;//这个表示连续字符的个数
for(int j=0;j<arr[i-1].size();j++)//用于扫描前一个对象,以此来生成此对象
{
char ch=arr[i-1][j];//保存一下字符
if(j+1<arr[i-1].size() && arr[i-1][j+1]==ch)//如果是连续的那么就多一个数目
{
index++;
}
else//直到不是连续的字符,直接进行对象对象
{
arr[i]+=(index+'0');//个数
arr[i]+=ch;//字符
index=1;//重新置为1
}
}
}
return arr[n-1];
}
};
LeetCode67 二进制加法
换汤不换药
class Solution {
public:
string addBinary(string a, string b)
{
string ret;
int end1=a.size()-1;
int end2=b.size()-1;
int next=0;
while(end1>=0 || end2>=0)
{
int ret1=0;
int ret2=0;
if(end1>=0)
{
ret1=a[end1--]-'0';
}
if(end2>=0)
{
ret2=b[end2--]-'0';
}
int retch=ret1+ret2+next;
if(retch>1)
{
retch-=2;//进位
next=1;
}
else
next=0;
ret+=(retch+'0');
}
if(next==1)
{
ret+='1';
}
reverse(ret.begin(),ret.end());
return ret;
}
};
这道题其实和“第一个唯一的字符”那个题基本一致,都是进行映射
class Solution {
public:
bool canConstruct(string ransomNote, string magazine)
{
if(ransomNote=="")//如果ransomnote为空,则返回true
return true;
if(magazine=="")//如果magazine为空,则返回flase
return false;
int* count=new int[26];//开辟存放26个字母的数组
for(int i=0;i<26;i++)
{
count[i]=0;
}
for(int i=0;i<magazine.size();i++)//映射
{
count[magazine[i]-'a']++;
}
for(int j=0;j<ransomNote.size();j++)//只要有的全部对应减去
{
if(count[ransomNote[j]-'a']>0)
{
count[ransomNote[j]-'a']--;
}
else//一旦小于等于0,肯定不存在
return false;
}
return true;
}
};
此题非常简单,我写得有点麻烦,不过可读性很强
class Solution {
public:
int countSegments(string s)
{
if(s=="")//如果是空字符返回0
return 0;
int num=0;//记录字符总数
int start=0;//判断非空格
int non_sapace=0;//消除空格
int end=s.size()-1;
while(s[end]==' ')//消除后面的空格,直到最后一个单词末尾
{
if(end==start && s[end]==' ')//如果end=start并且这个字符还是空那么就是0
return 0;
end--;
}
while(start<=end && non_sapace<=end)//扫描
{
while(s[non_sapace]==' ')//先用nonspace消除空格
non_sapace++;
start=non_sapace;//找到某个单词的第一个位置
while(start<=end && s[start]!=' ')//扫描完整个单词
{
start++;
}
num++;//表示单词+1
non_sapace=start;//循环,用non——space消除空格
}
return num;
}
};
LeetCode459:重复的子字符串
这道题有必要好好说明一下
我们把字符串s中循环的子字符串称为循环节,题目就是要让我们判断一个字符串有没有循环节
如果没有循环节,我们两个字符串拼接到一起,称为ss,那么ss中肯定有两个s,那么从ss中寻找s,必然能找到,并且返回值一定是s.size()
如果有循环节,那么设循环节的长度为len,那么ss中必然有ss.size()/len+1个循环节,然后“去掉ss中的第一个字符”,也就是从ss的第二个位置开始寻找s,那么既然有循环节,所以如果其返回值不是s.size()的话那么就能证明成功
所以直接可以用一行代码解决
class Solution {
public:
bool repeatedSubstringPattern(string s)
{
return (s+s).find(s,1)!=s.size();
}
};
LeetCode01.03:URL化
这道题和剑指offer的面试题5:替换空格基本相似,但是这道题中给出的是真实的字符串的长度,这一点是需要十分注意的
string replaceSpaces(string S, int length)
{
int length_bak=length;
int i=0;
int real_space=0;;
while(length_bak--)//统计有效的空格
{
if(S[i]==' ')
real_space++;
i++;
}
string ret;
ret.resize(length+2*real_space+1);//多申请一个空格存放'\0'
int j=length-1;
int k=ret.size()-2;//这里-2
while(k>=0)
{
if(S[j]!=' ')
{
ret[k--]=S[j--];
}
else
{
ret[k--]='0';
ret[k--]='2';
ret[k--]='%';
j--;
}
}
return ret;
}
LeetCode551:学生出勤记录I
此题较为简单。注意我们判断时,只需判断LLL是否是S的字符串,如果是那肯定false,如果不是就表明s中不可能存在LLL,LLLLL这样的字符串,最多也只能是LL
class Solution {
public:
bool checkRecord(string s)
{
int A_number=0;
if(strstr(s.c_str(),"LLL")!=NULL)
{
return false;
}
else
{
for(int i=0;i<s.size();i++)
{
if(s[i]=='A')
++A_number;
if(A_number>1)
return false;
}
}
return true;
}
};
LeetCode925:长按键入
这道题解决时使用for循环去遍历typed,然后如果对应相同就同步扫描,然后如果不对应相同,那么就看一下此处typed[j]和name[i-1]是否相同,如果相同那么说明typed[j]就是被长按出来的
class Solution {
public:
bool isLongPressedName(string name, string typed)
{
int i=0;
int j=0;
for(j=0;j<typed.size();j++)
{
if(name[i]==typed[j])//对应相等,同步扫描
i++;
else if(i-1>=0 && typed[j]==name[i-1])//如果不对应相等,看下typed[j]是否是被长按的
{
continue;
}
else//除此之外全部是flase
return false;
}
if(i>name.size()-1)//防止name长,typede短的情况发生
return true;
else
return false;
}
};
LeetCode 859:亲密字符串
这道题需要考虑情况较多。观察其特点,如果是亲密字符串,最多有两个字符位置不同
class Solution {
public:
bool buddyStrings(string a, string b)
{
if(a.size()!=b.size())//如果长度不相同,肯定false
return false;
string differ_a;
string differ_b;//分别用两个字符串记录a和b不同的字母
int count=0;//用来记录有几个不同的字母
for(int i=0;i<a.size();i++)
{
if(a[i]!=b[i])//如果不相同
{
differ_a+=a[i];
differ_b+=b[i];//就把对应的字母依次放在两个字符数组中
count+=1;//说明不相同的字符读多一个
}
if(count>2)//一旦出现两个以上不同的字符,那么肯定不是亲密字符串
return false;
}
if(count==2 && differ_a[0]==differ_b[1] && differ_a[1]==differ_b[0])//然后如果是两个字符不同,再看数组a[0]是否等于b[1],a[1]是否等于b[0]
return true;
if(count==0)//如果count等于0,说明两个字符串相同,然后在这里,aa和aa返回的是true,ab和ab却返回的是false,
//所以就看一下a字符串里里面是否会有相同的字符,如果有那么就能交换因此是亲密。比如说abab和abab
{
int arr[26]={0};//根据字母映射关系进行
for(int i=0;i<a.size();i++)
{
if(arr[a[i]-'a']==1)
return true;
arr[a[i]-'a']=1;
}
return false;
}
return false;
}
};