LeetCode刷题 字符串详解
- 一、字符串常用的操作
- 二、LeetCode实战
一、字符串常用的操作
1. string类
1.1 string的定义与初始化
1.1.1 string的定义
string str; //定义了一个string对象
string str[num] //定义了一个大小为num的string数组,数组元素为string
1.1.2 string的初始化
对于string的多种初始化方式取决于string类的多种构造函数
下面我们分别来举例说明:
string s; //默认构造函数,s为空
string s(s1); //s1是一个string类,s被初始化为s1的拷贝
string s=s1; //与string s(s1)等价
string s{c1,c2,c3,···}; //s初始化为元素列表的拷贝
string s={c1,c2,c3,···}; //与string s{c1,c2,c3,···}等价
string s(b,e); //s初始化为迭代器b,e指定范围中的元素,范围为[b:e)
string s(n); //s初始化大小为n,n个元素全部默认初始化为空
string s(n,c); //s初初始化大小为n,n个元素都被初始化为c
string s(cp,n); //s是cp指向的数组前n个字符的拷贝
string s(s2,pos2); //s是s2从下标pos开始的字符拷贝
string s(s2,pos2,len2); //s是s2从下标pos开始长度为len2的字符拷贝
1.2 string的赋值与swap、大小操作、关系运算
C++的标准模板库中重载了许多运算符,这使得我们对于string类的许多操作都比较方便、直观
1.2.1 string的赋值与swap
s1=s2; //将s1中的元素替换为s2中的元素
s={c1,c2,c3,···}; //将s中的元素替换为列表中的元素
swap(s1,s2); //交换s1、s2中的元素,此时s1,s2互换,比s1=s2要快的多
s1.swap(s2); //与swap(s1,s2)等价
//assign方法,不适用关联容器和array
s.assign(b,e); //将s中的元素替换为迭代器b、e确定的范围之间的元素
s.assign(il); //将s中的元素替换为初始化列表il中的元素
s.assign(n,c); //将s中的元素替换为n个c
1.2.2 string的大小操作
s.size(); //返回值为s中元素个数
s.length(); //返回值为s的长度,也就是s中元素个数
s.empty(); //如果s为空,返回true;否则返回false
s.max_size(); //返回一个s能容纳的最大元素数
1.2.3 string的关系运算
关系运算主要是包含了==、!=、>、>=、<、<=这 6 种运算符,并且对于比较两个string类,实际上就是逐个比较两个string类中的元素
这些关系比较的规则简述如下:
- 如果两个string类具有相同大小且所有元素都两两对应相等,则两个string类相等;否则不相等
- 如果两个string类的大小不同,但是较小的string类中的元素都等于较大string类的对应位置的元素,则较小的string类大于较大的string类
- 如果两个string类都不是另一个string类的子序列,则比较情况取决于第一个不相同的元素
- string里面的元素大小是按ASIIC码进行比较大小的
1.3 string的访问、插入、删除
我们这里对于string的修改只考虑了删除和插入,对于字符串的替换操作replace暂不考虑,可以自己查找相关资料
1.3.1 string的访问
对于访问string类中的元素有很多种方法,for循环语句、迭代器、下标访问、通过相应的成员函数访问
//for循环语句
for(auto c : s) cout<<c<<endl;
//通过迭代器进行访问
auto b=s.begin(),e=s.end();
while(b!=e)
{
cout<<*b<<endl; //注意这里的b,e是迭代器,要进行访问值必须解引用
b++;
}
//下标访问
int len=s.size(); //或者是int len=s.length()
for(int i=0;i<len;++i)
cout<<s[i]<<endl; //或者是cout<<s.at(i)<<endl;
//相应的成员函数
s.front(); //返回第一个元素引用,使用前要判断非空
s.back(); //返回最后一个元素引用,使用前判断非空
1.3.2 string的插入
s.push_back(c); //向s尾部插入元素c
s.emplace_back(c); //与s.push_back(c)等价
//insert成员函数如果接受的第一个参数是一个迭代器,则其后的参数是用来构造一个string的,然后插入到迭代器指定的元素之前
s.insert(p,c); //在迭代器p指向的元素之前插入元素c,插入之后p失效,会返回一个指向新元素的迭代器
s.insert(p,n,c); //在迭代器p指向的元素之前插入n个c,插入之后p失效,会返回一个指向新添加的第一个元素的迭代器
s.insert(p,b,e); //在迭代器p指向的元素之前插入由迭代器b,e确定范围的所有元素,插入之后p失效,会返回一个指向新元素第一个的迭代器
s.insert(p,il); //在迭代器p指向的元素之前插入il的所有元素,插入之后p失效,并返回一个迭代器指向新插入元素的第一个元素
//insert成员函数如果接受的第一个参数是下标,则其后多是一些字符串的拷贝
s.insert(pos,str);
s.insert(pos,str,p,len);
s.insert(pos,cp,len);
s.insert(pos,n,c);
//args表示能够提供构造string的参数
s.append(args);
s.assign(args);
1.3.3 string的删除
s.pop_back(); //将s尾部的元素删除
s.erase(p); //删除迭代器p指定的元素,删除玩之后p失效,返回一个指向原位置元素处的迭代器
s.erase(b,e); //删除迭代器b、e所确定的范围内的所有元素,删除之后吧b、e失效,返回一个指向最后一个被删除的元素之后的元素的迭代器
s.clear(); //清除s中所有的元素,返回void
1.4 string类的搜索操作
string类提供了6个不同的搜索函数,每个函数都有4个重载版本
args的形式
c,pos //从s中位置pos开始查找字符c,pos默认为0
s2,pos //从s中位置pos开始查找字符串s2,pos默认为0
cp,pos //从s中位置pos开始查找指针cp指向的以空字符结尾的C风格字符串,pos默认为0
cp,pos,n //从s中位置pos开始查找指针cp指向的数组的前n个字符,pos和n无默认值
string的搜索操作
s.find(args) //查找s中args第一次出现的位置
s.rfind(args) //查找s中args最后一次出现的位置
s.find_first_of(args) //在s中查找args中任何一个字符第一次出现的位置
s.find_last_of(args) //在s中查找args中任何一个字符最后一次出现的位置
s.find_first_not_of(args) //在s中查找第一个不在args中的字符
s.find_last_not_of(args) //在s中查找最后一个不在args中的字符
1.5 string的compare函数
除了关系运算符,string类提供了compare成员函数用来比较,这与 C 标准库 strcmp函数相类似
s.compare()函数返回0、整数 或 负数,且有 6 中参数形式
s.compare的参数形式
s2 //比较s和s2
pos1,n1,s2 //将s中从pos1开始的n1个字符与s2进行比较
pos1,n1,s2,pos2,n2 //将s中从pos1开始的n1个字符与s2中从pos2开始的n2个字符进行比较
cp //比较s与cp指向的以空字符结尾的字符数组
pos1,n1,cp //将s中从pos1开始的n1个字符与cp指向的以空字符结尾的字符数组进行比较
pos1,n1,cp,n2 //将s中从pos1开始的n1个字符与指针cp指向的地址开始的n2个字符进行比较
1.6 string类的数值转换
string提供了字符向数值进行转化的成员函数,新标准也提供了一个数值向string进行转换的函数
to_string(val) //一组重载函数,返回val的string表示,val可以是任何算术类型
//返回s的起始字串表示的数值,b为转换所用基数,默认为10;p是size_t指针,表示s中第一个非数值下标,默认为0,表示函数不保存下标
stoi(s,p,b)
stol(s,p,b)
stof(s,p)
stod(s,p)
二、LeetCode实战
LC 38. 外观数列
给定一个正整数 n ,输出外观数列的第 n 项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
- countAndSay(1) = "1"
- countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。
前五项如下:
1
11
21
1211
111221
第一项是数字 1
描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 "11"
描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 "21"
描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 "1211"
描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 "111221"要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述组。要将描述转换为数字字符串,先将每组中的字符数量用数字替换,再将所有描述组连接起来。
提示:
- 1<=n<=30
解法思路一 递归
题目已经说明了这是一个递归定义式,countAndSay(n)是对countAndSay(n-1)的描述,我们可以假定该函数返回的是正确答案,然后进行递归求解,注意我们在输出countAndSay(n)的时候是需要用到countAndSay(n-1)的结果,这样才能构成递归
解题步骤
- 使用countAndSay(n-1)获取第n-1个答案s1,然后扫描s1
- 使用双指针进行扫描,并把结果存储在dp中
- 确定递归终止条件,即n==1时,返回"1"
代码
class Solution {
public:
string countAndSay(int n) {
if(n<=1) return "1";
string s1=countAndSay(n-1);
string dp="";
int i=0,j=0,len=s1.size();
while(i<len && j<len)
{
while(j<len && s1[i]==s1[j]) j++;
ans=ans+std::to_string(j-i)+s1[i];
i=j;
}
return dp;
}
};
解法思路二 动态规划
我们完全可以用一个string dp来进行保存第n-1个答案,然后循环执行下去直到第n个答案
解题步骤
该问的解题思想和递归差不多,其核心都是用双指针来进行扫描整个字符串,然后给出答案,这里不做过多的解释。
代码
class Solution {
public:
string countAndSay(int n) {
string dp,ans{'1'};
for(int i=1;i<n;++i)
{
int len=ans.size();
int l=0,r=0;
while(r<len)
{
while(r<len && ans[l]==ans[r]) r++;
dp=dp+to_string(r-l)+ans[l];
l=r;
}
ans=dp;
dp.clear();
}
return ans;
}
};
LC 49. 字母异位词分组
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
说明:
- 所有输入均为小写字母。
- 不考虑答案输出的顺序。
解法思路
这个题是要求我们将相同字母但不同字母排序的单词进行归类,由于不同字符排列的组合会有很多种,我们不便于比较,暴力解法会导致时间复杂度极高。
注意: 我们虽然无法罗列字母排序的所有情况,从而来进行比较,但是我们可以 自己规定一个顺序 然后将所有字母按照我们规定的顺序进行排列,那么具有相同字母的单词就会得到一个统一的排列,进而我们使用hash表就比较容易统计记录
解题步骤
- 将单词排序
- 构建hash表,其中hash表的key为排序后的单词,value为未排序的单词集合
- 遍历给定的数组
- 将hash表的集合进行整理合并,返回答案
代码
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>> hash;
for(int i=0;i<strs.size();i++)
{
string s=strs[i];
sort(s.begin(),s.end());
hash[s].push_back(strs[i]);
}
vector<vector<string>> ans;
for(auto item : hash)
ans.push_back(item.second);
return ans;
}
};
LC 151. 翻转字符串里的单词
给定一个字符串,逐个翻转字符串中的每个单词。
说明:
- 无空格字符构成一个 单词 。
- 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
- 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
解法思路一 自后向前扫描
对于字符串的反转,我们已经知道有reverse(b,e)函数来进行反转,但是这个函数是对每一个字符进行反转,而本题是要求单词进行反转。我们很容易想到的一个方法是从s的末尾开始进行扫描,然后新构造一个string类ans,然后将自后向前扫描的单词添加到ans里
上面介绍的方法确实可行,不过利用已知的reverse函数,我们也可以解决,即:两次反转
解题步骤
- 构造一个空string类 ans
- 自后向前扫描s,每扫描到一个单词就将该单词添加到ans末尾
- 扫描完成,返回结果 ans
代码
class Solution {
public:
string reverseWords(string s) {
string ans;
int l=s.size()-1,r=l;
while(l>=0)
{
while(l>=0 && s[l]==' ') l--,r--;
if(l<0) break;
while(l>=0 && s[l]!=' ') l--;
for(int i=l+1;i<=r;++i)
ans.push_back(s[i]);
ans.push_back(' ');
r=l;
}
ans.erase(ans.end()-1);
return ans;
}
};
解法思路二 两次反转(reverse)
我们知道reverse函数是将字符反转,也就是说当我们使用reverse之后,我们会得到一个反转字符串,但同时里面的单词也是反转的,这时如果我们能将每个单词进行再一次的反转,那么,单词就会变回原来的正确形式,这就是两次反转的用处
解题步骤
- 反转s中的每一个单词,并将其移动到s的前面,因为可能有多余的空格
- 将s中多余的空间删除
- 反转整个字符串
- 返回结果
代码
class Solution {
public:
string reverseWords(string s) {
int l=0,r=0,start=0;
int len=s.size();
while(r<len)
{
while(r<len && s[r]==' ') r++,l++;
if(r==len) break;
while(r<len && s[r]!=' ') r++;
reverse(s.begin()+l,s.begin()+r);
while(l<r) s[start++]=s[l++];
s[start++]=' ';
}
s.erase(s.begin()+start-1,s.end());
reverse(s.begin(),s.end());
return s;
}
};
LC 165. 比较版本号
给你两个版本号 version1 和 version2 ,请你比较它们。
版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由 多位数字 组成,可能包含 前导零 。每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。
比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。
返回规则如下:
- 如果 version1 > version2 返回 1,
- 如果 version1 < version2 返回 -1,
- 除此之外返回 0。
提示:
- 1 <= version1.length, version2.length <= 500
- version1 和 version2 仅包含数字和 '.'
- version1 和 version2 都是 有效版本号
- version1 和 version2 的所有修订号都可以存储在 32 位整数 中
解法思路
本题主要想考的并不是字符串的比较,因为版本号是用字符串的形式,所以比较容易误导大家往字符串比较方面去想。但是,我们应该注意到,本题用来比较的字符都是数字,并且比较规则与数值比较相同,所以,我们应该想到 字符串的数值转换,我们要先将字符串转换为数值,再进行比较
解题步骤
- 找到两个点之间的字符串
- 将其转换为数值,进行比较
- 按照比较规则进行返回或者继续循环
代码
class Solution {
public:
int compareVersion(string version1, string version2) {
int i=0,j=0,k1=0,k2=0;
int len1=version1.size(),len2=version2.size();
while(i<len1 && j<len2)
{
while(i<len1 && version1[i]!='.') i++;
while(j<len2 && version2[j]!='.') j++;
int num1=stoi(version1.substr(k1,i-k1));
int num2=stoi(version2.substr(k2,j-k2));
if(num1>num2) return 1;
else if(num1<num2) return -1;
i++,j++;
k1=i,k2=j;
}
for(;i<len1;++i)
{
if(version1[i]!='0' && version1[i]!='.') return 1;
}
for(;j<len2;++j)
{
if(version2[j]!='0' && version2[j]!='.') return -1;
}
return 0;
}
};
LC 929. 独特的电子邮件地址
每封电子邮件都由一个本地名称和一个域名组成,以 @ 符号分隔。
例如,在 alice@leetcode.com中, alice 是本地名称,而 leetcode.com 是域名。
除了小写字母,这些电子邮件还可能包含 '.' 或 '+'。
如果在电子邮件地址的本地名称部分中的某些字符之间添加句点('.'),则发往那里的邮件将会转发到本地名称中没有点的同一地址。例如,"alice.z@leetcode.com” 和 “alicez@leetcode.com” 会转发到同一电子邮件地址。 (请注意,此规则不适用于域名。)
如果在本地名称中添加加号('+'),则会忽略第一个加号后面的所有内容。这允许过滤某些电子邮件,例如 m.y+name@email.com 将转发到 my@email.com。 (同样,此规则不适用于域名。)
可以同时使用这两个规则。
给定电子邮件列表 emails,我们会向列表中的每个地址发送一封电子邮件。实际收到邮件的不同地址有多少?
提示:
- 1 <= emails[i].length <= 100
- 1 <= emails.length <= 100
- 每封 emails[i] 都包含有且仅有一个 '@' 字符。
解法思路
本题比较容易,使用集合,并且按规则对给出的邮件地址进行处理,处理为不含'+' '.'的形式,然后添加到set中,最后返回set的大小即可
解题步骤
- 循环遍历vector中的email地址
- 度地址进行处理,去除'.' '+'
- 将处理好的地址添加到集合hash中去
- 返回hash集合的大小
代码
class Solution {
public:
int numUniqueEmails(vector<string>& emails) {
unordered_set<string> hash;
for(int i=0;i<emails.size();++i)
{
int at=0;
string email=emails[i];
string name;
for(;at<email.size();++at)
{
if(email[at]=='@') break;
if(email[at]=='+')
{
while(email[at]!='@') at++;
break;
}
if(email[at]=='.') continue;
name.push_back(email[at]);
}
hash.insert(name+email.substr(at));
}
return hash.size();
}
};
LC 5. 最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
提示:
- 1 <= s.length <= 1000
- s 仅由数字和英文字母(大写和/或小写)组成
解题思路
本题是通过 枚举 所有回文字串的中心,找出最长的那一个
注意:
- 是枚举所有回文串的 中心
- 回文串长度可能是奇数或者偶数
解题步骤
- 枚举所有奇数长度回文串的中心,并记录最长的为s1
- 枚举所有偶数长度的回文串的中心,并记录最长的为s2
- 比较s1、s2,返回较长的那一个
代码
版本一
class Solution {
public:
string longestPalindrome(string s) {
int at=0,l=0,tag=0;
int len=s.size();
for(int i=0;i<len;++i)
{
int k=0;
while(i-k>=0 && i+k<len && s[i-k]==s[i+k]) k++;
if(tag==0)
{
if(l<k) at=i,l=k,tag=0;
}
else
{
if(2*l<2*k-1) at=i,l=k,tag=0;
}
k=0;
while(i-k>=0 && i+1+k<len && s[i-k]==s[i+1+k]) k++;
if(tag==0)
{
if(2*l-1<2*k) at=i,l=k,tag=1;
}
else
{
if(l<k) at=i,l=k,tag=1;
}
}
if(tag==0) return s.substr(at-l+1,2*l-1);
return s.substr(at-l+1,2*l);
}
};
版本二
class Solution {
public:
string longestPalindrome(string s) {
string ans;
int len=s.size();
for(int i=0;i<len;++i)
{
int k=0;
while(i-k>=0 && i+k<len && s[i-k]==s[i+k]) k++;
ans=ans.size()<2*k-1?s.substr(i-k+1,2*k-1):ans;
k=0;
while(i-k>=0 && i+1+k<len && s[i-k]==s[i+1+k]) k++;
ans=ans.size()<2*k?s.substr(i-k+1,2*k):ans;
}
return ans;
}
};
LC 6. Z 字形变换
将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:
P....A....H....N
A.P.L.S..I..I..G
Y.....I....R之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比> 如:"PAHNAPLSIIGYIR"。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
解题思路
本题主要就是找规律,我们可以看到是2row-2为一个周期,按此循环添加就行*
解题步骤
过程比较简单,不做解释
代码
class Solution {
public:
string convert(string s, int numRows) {
if(numRows==1) return s;
vector<string> res(numRows,"");
int len=s.size();
for(int i=0;i<len;++i)
{
if(i%(2*numRows-2)<numRows) res[i%(2*numRows-2)].push_back(s[i]);
else res[2*numRows-i%(2*numRows-2)-2].push_back(s[i]);
}
string ans;
for(auto item : res) ans+=item;
return ans;
}
};
LC 3. 无重复字符的最长子串
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
提示:
- 0 <= s.length <= 5 * 104
- s 由英文字母、数字、符号和空格组成
解题思路
本题的暴力解法是 枚举 所有起点开始的不重复字串,然后取最长,这样的时间复杂度是达到 O(n^2)。时间复杂度太高,我们其实可以将其进行降低,其实我们在枚举一个起点的时候,可以利用前面已经扫描过的字符串,我们就可以降低我们的时间复杂度为O(n)
我们的具体做法是,通过一个hash表来记录我们已经出现过的字符的出现频数,如果出现的次数<=1,我们的快指针就可以继续向后扫描,同时记录当前的最大长度;如果出现的次数>1,我们的慢指针必须向后移动,否则,快慢指针之间一定有重复字符;当快指针达到s末尾,即可得到最大字串的长度
解题步骤
- 构造hash表
- 创建快慢指针进行扫描
- 快指针读进一个字符,然后判断快指针指向的字符是否出现频次 >1 ;if true,慢指针++;else 记录无重复字符串的长度
- 当快指针到达s尾部,再次记录无重复字串长度,然后返回
代码
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int> hash;
int res=0;
for(int i=0,j=0;i<s.size();++i)
{
hash[s[i]]++;
while(hash[s[i]]>1) hash[s[j++]]--;
res=max(res,i-j+1);
}
return res;
}
};
LC 208. 实现 Trie (前缀树)
Trie(发音类似 "try")或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
- Trie() 初始化前缀树对象。
- void insert(String word) 向前缀树中插入字符串 word 。
- boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
- boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
提示:
- 1 <= word.length, prefix.length <= 2000
- word 和 prefix 仅由小写英文字母组成
- insert、search 和 startsWith 调用次数 总计 不超过 3 * 104 次
代码参考
本题只给出代码参考,详细自己体会
class Node {
public:
Node *nxt[26];
bool end;
Node() {
memset(nxt, NULL, sizeof(nxt));
end = false;
}
};
class Trie {
public:
/** Initialize your data structure here. */
Node *root;
Trie() {
root = new Node();
}
~Trie() {
queue<Node*> q;
q.push(root);
while (!q.empty()) {
Node *u = q.front();
q.pop();
for (int i = 0; i < 26; i++)
if (u->nxt[i])
q.push(u->nxt[i]);
delete u;
}
}
/** Inserts a word into the trie. */
void insert(string word) {
Node *p = root;
for (int i = 0; i < word.length(); i++) {
if (p->nxt[word[i] - 'a'] == NULL)
p->nxt[word[i] - 'a'] = new Node();
p = p->nxt[word[i] - 'a'];
}
p->end = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
Node *p = root;
for (int i = 0; i < word.length(); i++) {
if (p->nxt[word[i] - 'a'] == NULL)
return false;
p = p->nxt[word[i] - 'a'];
}
return p->end;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Node *p = root;
for (int i = 0; i < prefix.length(); i++) {
if (p->nxt[prefix[i] - 'a'] == NULL)
return false;
p = p->nxt[prefix[i] - 'a'];
}
return true;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie obj = new Trie();
* obj.insert(word);
* bool param_2 = obj.search(word);
* bool param_3 = obj.startsWith(prefix);
*/
LC 273. 整数转换英文表示
将非负整数 num 转换为其对应的英文表示。
提示:
- 0 <= num <= 231 - 1
题目思路
对于本题其实考验的就是一个字符串的组合拼接问题,需要根据不同的情况来进行,看代码自行体会
代码
class Solution {
public:
// 预先构建一些数组 0-19
string special[20] = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", \
"Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"};
// 十位数
string tens[10] = {"", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
// 千位数,百万位,十亿
string throusands[4] = {"", "Thousand", "Million", "Billion"};
string numberToWords(int num) {
// 考虑边缘情况
if (num == 0)
{
return "Zero";
}
string res = "";
// 倒序来排列
int index = 0;
while (num > 0)
{
// 忽略能完全能整除的情况
if (num % 1000 > 0)
{
if (index == 0)
{
res = helper(num % 1000);
}
else
{
res = helper(num % 1000) + " " + throusands[index] + (res != "" ? (" " + res) : "");
}
}
num /= 1000;
++index;
}
return res;
}
// 解决三维数字
string helper(int num)
{
// cout << num << endl;
if (num < 20)
{
return special[num];
}
else if (num >= 20 && num <= 99)
{
// 构建 Thrity One
return tens[num/10] + (num%10 != 0 ? (" " + special[num%10]): "");
}
else
{
string one = helper(num%100);
// cout << "[" << one << "]" << endl;
return special[num/100] + " Hundred" + (one != "" ? (" " + one) : "");
}
}
};
LeetCode刷题 字符串详解的更多相关文章
- C#LeetCode刷题-字符串
字符串篇 # 题名 刷题 通过率 难度 3 无重复字符的最长子串 24.6% 中等 5 最长回文子串 22.4% 中等 6 Z字形变换 35.8% 中等 8 字符串转整数 (atoi) ...
- buuctfweb刷题wp详解及知识整理----[安洵杯 2019]easy_web
尝试之路加wp 观察源代码和get所传参数可猜测img所传参数img就是该图片经过两次base64编码和一次hex编码后可得555.png成果验证猜测 然后发现该图片以data元数据封装的方式放到了源 ...
- LeetCode刷题指南(字符串)
作者:CYC2018 文章链接:https://github.com/CyC2018/CS-Notes/blob/master/docs/notes/Leetcode+%E9%A2%98%E8%A7% ...
- C#LeetCode刷题之#205-同构字符串(Isomorphic Strings)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3770 访问. 给定两个字符串 s 和 t,判断它们是否是同构的. ...
- C#LeetCode刷题-哈希表
哈希表篇 # 题名 刷题 通过率 难度 1 两数之和 C#LeetCode刷题之#1-两数之和(Two Sum) 42.8% 简单 3 无重复字符的最长子串 24.2% 中等 18 四数之和 ...
- LeetCode刷题专栏第一篇--思维导图&时间安排
昨天是元宵节,过完元宵节相当于这个年正式过完了.不知道大家有没有投入继续投入紧张的学习工作中.年前我想开一个Leetcode刷题专栏,于是发了一个投票想了解大家的需求征集意见.投票于2019年2月1日 ...
- Leetcode刷题记录(python3)
Leetcode刷题记录(python3) 顺序刷题 1~5 ---1.两数之和 ---2.两数相加 ---3. 无重复字符的最长子串 ---4.寻找两个有序数组的中位数 ---5.最长回文子串 6- ...
- LeetCode刷题总结-数组篇(下)
本期讲O(n)类型问题,共14题.3道简单题,9道中等题,2道困难题.数组篇共归纳总结了50题,本篇是数组篇的最后一篇.其他三个篇章可参考: LeetCode刷题总结-数组篇(上),子数组问题(共17 ...
- LeetCode刷题总结-树篇(中)
本篇接着<LeetCode刷题总结-树篇(上)>,讲解有关树的类型相关考点的习题,本期共收录17道题,1道简单题,10道中等题,6道困难题. 在LeetCode题库中,考察到的不同种类的树 ...
随机推荐
- 10分钟教你使用Picgo+GitHub+ jsDelivr搭建CDN加速免费图床
前言 经常写Markdown或者博客的同学,肯定都要用到图床.图床是什么呢?其实相当于一个存储图片的网站,类似百度云这样,不过上传图片到图床后可以直接通过外链进行访问. 比如把本地一张a.jpg上传到 ...
- 技术与艺术的结合,HMS Core让手机主题趣味丛生
在9月23日晚举办的华为nova9系列新品发布会上,华为在发布nova9系列新机之外,还为观众展示了多款Harmony OS趣味主题.其中一款名为"翻滚吧牛奶"的应用主题看起来十分 ...
- ASP.NET Core 学习笔记 第二篇 依赖注入
前言 ASP.NET Core 应用在启动过程中会依赖各种组件提供服务,而这些组件会以接口的形式标准化,这些组件这就是我们所说的服务,ASP.NET Core框架建立在一个底层的依赖注入框架之上,它使 ...
- Markdown 编写技巧汇总(一)
编写文档,有很多格式选择,也有不同平台选择.下面就自己接触到的MarkDown编写文档的各种技巧做简单梳理,供自己参阅,也希望帮到网友. [1]添加空格 ① 这种写法比较老土,但是,很实用!注意都 ...
- 【图像处理】基于OpenCV实现图像直方图的原理
背景 图像的直方图是衡量图像像素分布的一种方式,可以通过分析像素分布,使用直方图均衡化对图像进行优化,让图像变的清晰. opencv官方对图像直方图的定义如下: 直方图是图像中像素强度分布的图形表达方 ...
- 反调试——7——CRC检测
反调试--7--CRC检测 CRC32: CRC的全称是循环冗余校验,作用是为了检测数据的完整性. CRC32的检测原理: 程序被编译后,代码段是固定的,因为已经被写死了. 我们在调试程序的时候,打断 ...
- 题解「BZOJ4310」跳蚤
题目传送门 Description 现在有一个长度为 \(n\) 的字符串,将其划分为 \(k\) 段,使得这 \(k\) 段每一段的字典序最大子串中字典序最大的字符串字典序尽量小.求出这个字符串. ...
- Vue3学习(五)之集成HTTP库axios
一.安装axios npm install axios@0.21.0 --save 二.axios的使用 1.在主页中引用axios 在Vue3新增了setup初始化方法,所以我们在这里开始使用并测试 ...
- [kuangbin带你飞]专题一 简单搜索 棋盘问题
题来:链接https://vjudge.net/problem/OpenJ_Bailian-132 J - 棋盘问题 1.题目: 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别. ...
- Java(1)开发环境配置及第一个程序Hello World
作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201468.html 博客主页:https://www.cnblogs.com/testero ...