文章目录
38.外观数列
题意很简单就是每次对上一个字符串计数
不能用递归,一开始用爆栈了
就用两层循环更新答案
class Solution {
public:
string countAndSay(int n) {
string res="1"; //最终答案,初始化 为1
for(int i=0;i<n-1;i++){ //输入n应当更新n-1次
string ans; //每次更新的字符串
for(int j=0;j<res.size();){
char key=res[j]; //标记
int count=0; //计数
while(res[j]==key){ //循环到第一个不等于key的字符
j++;
count++;
}
ans+=to_string(count)+key;
}
res=ans; //替换
}
return res;
}
};
49.字母异位词分组 **
这题是哈希表的应用
我们建立这样一个从string映射到vector< string >的哈希表
将每个字符串排好序之后对应的字符串作为一个key,存放到哈希表的string中,并将对应的值存放到key对应的vector中
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> res;
unordered_map<string,vector<string>> hash;
for(auto str:strs){
string key=str;
sort(key.begin(),key.end()); //将映射的key进行排序
hash[key].push_back(move(str)); //每个排完序相同的字符串都将存到一个key值的vector中 ,,使用move避免重新复制字符串,直接改变指针指向str
}
for(auto it:hash)res.push_back(move(it.second)); //vector存入res中
return res;
}
};
151.翻转字符串里的单词 **
这题要求翻转字符串中的单词,并且不准开额外的空间
基本的思路是先翻转字符串的每个单词,再翻转字符串整体,通过reverse函数,效果如下,即可实现整个字符串的翻转
具体做法是使用k,i,j三个指针,i,j先指向字符串的每个单词,使其先翻转,再使用s[k++]=s[i++]从头开始对s赋值(ij是前进指针,k是跟进指针)
要注意该题的空格在代码中的解决
class Solution {
public:
string reverseWords(string s) {
int k=0; //从s[0]开始赋值,避免开头有空格
for(int i=0;i<s.size();i++){
while(s[i]==' '&&i<s.size())i++; //i前进到下一个不是空格的位置
if(i==s.size())break;
int j=i;
while(s[j]!=' '&&j<s.size())j++; //j前进到单词末尾,指向下一个空格
reverse(s.begin()+i,s.begin()+j); //翻转这个单词
if(k)s[k++]=' '; //在每个单词结束加上一个空格,第一个除外,最后一个空格由于if(i==s.size())break也无法加入
while(i<j)s[k++]=s[i++];
}
s.erase(s.begin()+k,s.end()); //收尾,删掉最后一个单词后面的空格
reverse(s.begin(),s.end()); //翻转整个字符串
return s;
}
};
165.比较版本号 ??
题目本身还是比较简单的,只不过在写法上需要点技巧和经验
思路:
小数点将每个字符串都分割成一段一段的数字,只要对应比较每一段数字大小就行。。这里用atoi函数转化成数字,手写也行
需要注意的是,如果两串分出的部分不一样多的话,(同长度的部分比较完依旧没有分出大小),那么继续比较,没有数的那个串看做是0就行了
- 使用的函数:
- substr(start,lenth), 截断字符串
- atoi(char*[]),字符数组转换成数字,需要传字符数组首地址
- c_str(null),将string转化成字符数组char* []并返回首地址
未解决:
while(x<s1.size()&&s1[x]!=’.’)x++;
while(y<s2.size()&&s2[y]!=’.’)y++; //交换&&的两个条件的顺序会爆溢出,不知道为什么
class Solution {
public:
int compareVersion(string s1, string s2) {
int i=0,j=0;
while(i<s1.size()||j<s2.size())
{
int x=i,y=j;
while(x<s1.size()&&s1[x]!='.')x++; //每次找到一串数字的终点
while(y<s2.size()&&s2[y]!='.')y++;
int a= i==x?0:atoi(s1.substr(i,x-i).c_str()); //将每一段转化为数字,如果该段长度为0或者不存在则返回0
int b= j==y?0:atoi(s2.substr(j,y-j).c_str());
i=x+1,j=y+1; //ij跳向'.'下一个位置
if(a>b)return 1;
if(a<b)return -1;
}
return 0;
}
};
929.独特的电子邮件地址
思路:按照题目要求简化每一个所给的邮件地址,得到新的邮件地址加入哈希表中,最后返回哈希表元素个数(哈希表自带判重功能)
class Solution {
public:
int numUniqueEmails(vector<string>& emails) {
unordered_set<string> hash; //无需映射的哈希表
for(auto email:emails){
int at=email.find('@'); //找到每个邮件地址@的位置
string domain=email.substr(at+1); //取出域名部分
string name;
for(auto it:email.substr(0,at)){ //简化本地名称部分
if(it=='+')break;
else if(it!='.')name+=it; //去掉里面的'.'
}
string s=name+'@'+domain; //构造简化后的邮件地址
hash.insert(s);
}
return hash.size();
}
};
5.最长回文子串**
该题使用暴力解法,直接枚举每一个字符,把他当做子串的对称中心点(如果最长回文子串是奇数长,则对称中心是一个点,偶数则是两个点)像两边延伸,找到最长的回文子串
class Solution {
public:
string longestPalindrome(string s) {
string res;
for(int i=0;i<s.size();i++){
for(int m=i,n=i; m>=0 && n<s.size()&& s[n]==s[m]; m--,n++) //找奇数对称最长串
if(res.size()<n-m+1)res=s.substr(m,n-m+1);
for(int m=i,n=i+1; m>=0 && n<s.size() && s[n]==s[m]; m--,n++) //找偶数对称最长串
if(res.size()<n-m+1)res=s.substr(m,n-m+1);
}
return res;
}
};
6.Z字形变换
y总做法是找规律,感觉也就这样。。
首先我们按规律找寻每一行元素的下标,然后依次添进结果字符串res中
首先第一行和最后一行都是公差为2*(n-1)的数列
其余的行分别是交错的两个公差为2*(n-1)等差数列
他们的开头分别是i,2*(n-1)-i
class Solution {
public:
string convert(string s, int n) {
if(n==1)return s;
string res;
for(int i=0;i<n;i++){ //列举当前行
if(!i||i==n-1) //列举如果是第一行或者最后一行的情况
for(int j=i;j<s.size();j+=2*(n-1))res+=s[j];
else{ //列举其他行(每行有两个等差数列)
for(int j=i,k=2*(n-1)-i;j<s.size()||k<s.size();j+=2*(n-1),k+=2*(n-1)){
if(j<s.size())res+=s[j]; //如果枚举在范围内,则加上该字符
if(k<s.size())res+=s[k];
}
}
}
return res;
}
};
附上自己的写法,想法很简单,就是按照要求吧字符串依次放进二维向量里再读出来,
- 二维向量初始化没有给定长度,所以报了空指针(?)
class Solution {
public:
string convert(string s, int numRows) {
int n=s.size();
vector<vector<char>> res(numRows);
int i=0;
while(i<n){
for(int j=0;j<numRows&&i<n;j++,i++) //填满一列
res[j].push_back(s[i]);
for(int k=numRows-2;k>0&&i<n;k--,i++) //斜着向上填
res[k].push_back(s[i]);
}
string ans;
for(auto items:res){ //二维向量遍历
for(auto item:items){
cout<<item<<" ";
ans+=item;
}
}
return ans;
}
};
3.无重复字符的最长子串 (双指针)
思路:双指针算法
- 前指针i,后指针j
- 我们保证当前维护的子串(i,j)没有重复元素,当前子串不可能向前拓展(即i不能往前移动)
- 依靠哈希表来判断维护的区间是否出现重复元素
- 每次j向后移动,扩展最长子串,那么可能会出现重复的元素,为此需要不断向后移动i,直到满足没有重复元素为止
- 更新最大子串
ps:如何满足i所在位置是当前最大子串位置?只需要i从头开始行进即可,不需要额外判断
菜鸡(O(n2))写法
class Solution {
public:
const int N=200;
int lengthOfLongestSubstring(string s) {
int res=0;
int flag[N];
for(int i=0;i<s.size();i++){ //外层循环n次,内层循环每次最多走n步,所以复杂度是O(n ^ 2)
memset(flag,0,sizeof(flag));
int j;
for( j=i;j<s.size()&&!flag[s[j]];j++)flag[s[j]]=true;
res=max(res,j-i);
}
return res;
}
};
O(n)写法
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> hash;
int res = 0;
for (int i = 0, j = 0; j < s.size(); j ++ ) //时间复杂度:虽然是双重循环,但由于i最多总共只向后走n步,平均到每一层循环走一步,那么时间复杂度是O(n)
{
hash[s[j]] ++ ;
while (hash[s[j]] > 1) hash[s[i ++ ]] -- ; //如果出现重复元素,让i往后走直到没有重复元素为止
res = max(res, j - i + 1);
}
return res;
}
};
208.实现Trie 树
目标是使用结构体的方式实现一个字典树,基础算法里实现过一个二维数组的Trie树,基本思路相同
首先初始化一个根节点root,根节点不存放数据,根节点存在26个子节点指针
- 插入:对于每个字符串,遍历插入树中,如果该节点不存在则初始化一个节点
并将结尾Node的is_end赋值为true- 查询:遍历当前字符串到其结尾,如果is_end存在则代表有该字符串存在
- 查询前缀:只要Trie树中存在前缀字符串即可
class Trie {
public:
struct Node{
bool is_end;
Node* son[26];
Node(){
is_end=false;
for(int i=0;i<26;i++)son[i]=NULL;
}
}*root; //root指向结构体Node
/** Initialize your data structure here. */
Trie() {
root=new Node();
}
/** Inserts a word into the trie. */
void insert(string word) {
auto p=root;
for(auto s:word){
int u=s-'a';
if(!p->son[u])p->son[u]=new Node(); //如果子节点不存在则初始化一个节点
p=p->son[u];
}
p->is_end=true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
auto p=root;
for(auto s:word){
int u=s-'a';
if(p->son[u])p=p->son[u];
else return false;
}
return p->is_end;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
auto p=root;
for(auto s:prefix){
int u=s-'a';
if(p->son[u])p=p->son[u];
else return false;
}
return true;
}
};
273.整数转化英文表示
这题不注意写法确实麻烦的一批,看了一下好像也有拿递归写的也还不错,但还是这个容易理解一点
思路:转化为每三个一位每三个一位的数,并且逐段翻译,每段中间加上相应的计数(billion,million,thousand)
class Solution {
public:
//映射到字符串数组中
string small[20]={"Zero","One","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten",
"Eleven","Twelve","Thirteen","Fourteen","Fifteen","Sixteen","Seventeen","Eighteen","Nineteen"};
string decade[10]={"","","Twenty","Thirty","Forty","Fifty","Sixty","Seventy","Eighty","Ninety"};
string big[4]={"Billion","Million","Thousand",""};
string numberToWords(int num) {
if(!num)return small[0];
string res;
for(int i=1000000000,j=0;i>0;i=i/1000,j++){ //从最高三位开始转换,每次转三位,最多四次
int n=num/i;
if(n)res+=helper(n)+big[j]+" ";
num%=i;
}
while(res.back()==' ')res.pop_back(); //删除最后字符串的多余空格
return res;
}
string helper(int n){ //每段的三位数转化为英文
string res;
if(n>=100){
res+=small[n/100]+" Hundred ";
n=n%100;
}
if(!n)return res;
if(n>=20){
res+=decade[n/10]+" ";
n=n%10;
if(!n)return res;
res+=small[n]+" ";
}
else res+=small[n]+" ";
return res;
}
};