LeetCode 38. Count and Say
报数序列是一个整数序列,按照其中的整数的顺序进行报数,得到下一个数。其前五项如下:
1. 1
2. 11
3. 21
4. 1211
5. 111221
1 被读作 “one 1” (“一个一”) , 即 11。
11 被读作 “two 1s” (“两个一”), 即 21。
21 被读作 “one 2”, “one 1” (“一个二” , “一个一”) , 即 1211。
给定一个正整数 n(1 ≤ n ≤ 30),输出报数序列的第 n 项。
注意:整数顺序将表示为一个字符串。
示例 1:
输入: 1
输出: “1”
示例 2:
输入: 4
输出: “1211”
class Solution {
public:
string countAndSay(int n) {
string s = "1";
for(int i = 1; i < n; i++)
{
string ns = "";//当前字符串
for(int j = 0; j < s.size();)
{
int u = j;
while(u < s.size() && s[u] == s[j]) u ++;
ns += to_string(u - j);//j是起始 u是重复的下标
ns += s[j];
j = u;//进行下一次操作
}
s = ns;
}
return s;
}
};
LeetCode 49. Group Anagrams
给定一个字符串数组,将字母异位词组合在一起。字母异位词指字母相同,但排列不同的字符串。
示例:
输入: [“eat”, “tea”, “tan”, “ate”, “nat”, “bat”],
输出:
[
[“ate”,“eat”,“tea”],
[“nat”,“tan”],
[“bat”]
]
说明:
所有输入均为小写字母。
不考虑答案输出的顺序。
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> hash;//把字符串映射到一个字符串集合
for(auto s : strs)
{
string rs = s;
sort(s.begin(), s.end());
hash[s].push_back(rs);//把所有字符串归类 统计相同个数
}
vector<vector<string>> res;
for(auto &group : hash)
{
res.push_back(group.second); //group.second是hash的第二维字符串集合
}
return res;
}
};
LeetCode 151. Reverse Words in a String
给定一个字符串,逐个翻转字符串中的每个单词。
示例 1:
输入: “the sky is blue”
输出: “blue is sky the”
示例 2:
输入: " hello world! "
输出: “world! hello”
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
示例 3:
输入: “a good example”
输出: “example good a”
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
说明:
无空格字符构成一个单词。
输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
class Solution {
public:
/*
分两步操作:
1.将字符串中的每个单词逆序,样例输入变为: "eht yks si eulb";
2.将整个字符串逆序,样例输入变为:"blue is sky the";
*/
string reverseWords(string s) {
int i = 0,k = 0;
s += " ";
while(k < s.size() && s[k] == ' ') k++;//过滤开头的空格
while(k < s.size())
{
if(s[k] != ' ')
s[i++] = s[k++];
else //过滤单词间的空格
{
s[i++] = ' ';//给一个空格
while(k < s.size() && s[k] == ' ') k ++;//过滤
}
}
if(!i)
{
s = "";
return s;
}
s.erase(s.begin() + i - 1, s.end()); //去掉结尾空格(因为)
for(int i = 0; i <s.size(); i++)
{
int j = i;
while(j < s.size() && s[j] != ' ') j ++;
reverse(s.begin() + i, s.begin() + j);//翻转每个单词
i = j; //操作下一个单词
}
reverse(s.begin(), s.end());
return s;
}
};
LeetCode 165. Compare Version Numbers
比较两个版本号 version1 和 version2。
如果 version1 > version2 返回 1,如果 version1 < version2 返回 -1, 除此之外返回 0。
你可以假设版本字符串非空,并且只包含数字和 . 字符。
. 字符不代表小数点,而是用于分隔数字序列。
例如,2.5 不是“两个半”,也不是“差一半到三”,而是第二版中的第五个小版本。
你可以假设版本号的每一级的默认修订版号为 0。例如,版本号 3.4 的第一级(大版本)和第二级(小版本)修订号分别为 3 和 4。其第三级和第四级修订号均为 0。
示例 1:
输入: version1 = “0.1”, version2 = “1.1”
输出: -1
示例 2:
输入: version1 = “1.0.1”, version2 = “1”
输出: 1
示例 3:
输入: version1 = “7.5.2.4”, version2 = “7.5.3”
输出: -1
示例 4:
输入:version1 = “1.01”, version2 = “1.001”
输出:0
解释:忽略前导零,“01” 和 “001” 表示相同的数字 “1”。
示例 5:
输入:version1 = “1.0”, version2 = “1.0.0”
输出:0
解释:version1 没有第三级修订号,这意味着它的第三级修订号默认为 “0”。
提示:
版本字符串由以点 (.) 分隔的数字字符串组成。这个数字字符串可能有前导零。
版本字符串不以点开始或结束,并且其中不会有两个连续的点。
class Solution {
public:
vector<int> get_num(string version)
{
vector<int> res;
for(int i = 0; i < version.size(); i++)
{
int j = i, s= 0;
while(j < version.size() && version[j] != '.')
{
s = s * 10 + version[j] - '0';
j ++;
}
i = j;//操作下一个 .
res.push_back(s);
}
while(res.size() && res.back() == 0) res.pop_back();//去除末尾无效 1.0 和 1
return res;
}
int compareVersion(string version1, string version2) {
auto n1 = get_num(version1);
auto n2 = get_num(version2);
if(n1 < n2) return -1;
if(n1 > n2) return 1;
else return 0;
}
};
LeetCode 5. Longest Palindromic Substring
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba” 也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
class Solution {
public:
string longestPalindrome(string s) {
int len = 0;
string ans;
for(int k = 0; k < s.size(); k ++)//枚举中心点 往两边扩展
{
int i = k, j = k + 1;//偶数情况
while(i >= 0 && j < s.size() && s[i] == s[j]) i--, j++;
if(j - i - 1 > len)
{
len = j - i - 1;
ans = s.substr(i + 1, len);
}
i = k - 1, j = k + 1;//奇数情况
while(i >= 0 && j < s.size() && s[i] == s[j]) i--, j++;
if(j - i - 1 > len)
{
len = j - i - 1;
ans = s.substr(i + 1, len);
}
}
return ans;
}
};
LeetCode 273. Integer to English Words
将非负整数转换为其对应的英文表示。可以保证给定输入小于 231 - 1 。
示例 1:
输入: 123
输出: “One Hundred Twenty Three”
示例 2:
输入: 12345
输出: “Twelve Thousand Three Hundred Forty Five”
示例 3:
输入: 1234567
输出: “One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven”
示例 4:
输入: 1234567891
输出: “One Billion Two Hundred Thirty Four Million Five Hundred Sixty Seven Thousand Eight Hundred Ninety One”
class Solution {
public:
int hundred = 100, thousand = 1000, million = 1000000, billion = 1000000000;
unordered_map<int, string> numbers;
string numberToWords(int num) {
char number20[][30] = {"Zero", "One", "Two", "Three", "Four", "Five",
"Six", "Seven", "Eight", "Nine", "Ten", "Eleven",
"Twelve", "Thirteen", "Fourteen", "Fifteen",
"Sixteen", "Seventeen", "Eighteen", "Nineteen"};
char number2[][30] = {"Twenty", "Thirty", "Forty", "Fifty", "Sixty",
"Seventy", "Eighty", "Ninety"};
for(int i = 0; i < 20; i++) numbers[i] = number20[i];//映射0-19
for(int i = 20, j = 0; i < 100; i += 10, j++) numbers[i] = number2[j];//映射二十三十这些
numbers[hundred] = "Hundred", numbers[thousand] = "Thousand";
numbers[million] = "Million", numbers[billion] = "Billion";
string res;
for(int k = 1000000000; k >= 100; k /= 1000)
{
if(num >= k)
{
res += ' ' + get3(num / k) + ' ' + numbers[k];
num %= k;
}
}
if(num) res += ' ' + get3(num);
if(res.empty()) res = ' ' + numbers[0];
return res.substr(1); //去除开头空格
}
string get3(int num)
{
string res;
if(num >= hundred)// >=
{
res += ' ' + numbers[num / hundred] + ' ' + numbers[hundred];
num %= hundred;
}
if(num)
{
if(num < 20) res += ' ' + numbers[num];
else if(num % 10 == 0) res += ' ' +numbers[num];
else res += ' ' + numbers[num / 10 * 10] + ' ' + numbers[num % 10];
}
return res.substr(1); //去除开头空格
}
};
LeetCode 6. ZigZag Conversion
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 “LEETCODEISHIRING” 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:“LCIRETOESIIGEDHN”。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入: s = “LEETCODEISHIRING”, numRows = 3
输出: “LCIRETOESIIGEDHN”
示例 2:
输入: s = “LEETCODEISHIRING”, numRows = 4
输出: “LDREOEIIECIHNTSG”
解释:
L D R
E O E I I
E C I H N
T S G
class Solution {
public:
/*
对于行数是 n的情况:
对于第一行和最后一行,是公差为 2(n−1)的等差数列,首项是 0 和 n−1;
对于第 i行(0<i<n−1),是两个公差为 2(n−1) 的等差数列交替排列,首项分别是 i 和 2n−i−2;
所以我们可以从上到下,依次打印每行的字符。
*/
string convert(string s, int numRows) {
string res;
if(numRows == 1) return s;
for(int j = 0; j < numRows; j++) //枚举每一行
{
if(j == 0 || j == numRows - 1) //第一行或者最后一行
{
for(int i = j; i < s.size(); i += 2 * (numRows - 1))
res += s[i];
}
else //中间行
{
for(int k = j, i = numRows * 2 - 1 - j - 1;
i < s.size() || k < s.size();
i += 2 * (numRows - 1), k += 2 * (numRows - 1))
{
if(k < s.size()) res += s[k];//交替输出
if(i < s.size()) res += s[i];
}
}
}
return res;
}
};
LeetCode 3. Longest Substring Without Repeating Characters
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: “abcabcbb”
输出: 3
解释: 因为无重复字符的最长子串是 “abc”,所以其长度为 3。
示例 2:
输入: “bbbbb”
输出: 1
解释: 因为无重复字符的最长子串是 “b”,所以其长度为 1。
示例 3:
输入: “pwwkew”
输出: 3
解释: 因为无重复字符的最长子串是 “wke”,所以其长度为 3。
请注意,你的答案必须是 子串 的长度,“pwke” 是一个子序列,不是子串。
class Solution {
public:
/*
两个指针 i,j,表示当前扫描到的子串是 [i,j]。unordered_map<char,int> hash,表示 [i,j]中每个字符出现的次数。
线性扫描时,每次循环的流程如下:
1.指针 j向后移一位, 同时将哈希表中 s[j]的计数加一: hash[s[j]]++;
2.假设 j移动前的区间 [i,j] 中没有重复字符,则 j 移动后,只有 s[j] 可能出现2次。因此我们不断向后移动 i,直至区间 [i,j]中 s[j] 的个数等于1为止;
*/
int lengthOfLongestSubstring(string s) {
unordered_map<char, int> hash;
int res = 0;
for(int i = 0, j = 0; j < s.size(); j++)
{
if(++ hash[s[j]] > 1) // s[j]出现2次, 需要缩小空间,让只出现一次
{
while(i < j)
{
hash[s[i]]--;
i ++;//i向后移
if(hash[s[j]] == 1) break;
}
}
res = max(res, j - i + 1);
}
return res;
}
};
LeetCode 166. Fraction to Recurring Decimal
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以字符串形式返回小数。
如果小数部分为循环小数,则将循环的部分括在括号内。
示例 1:
输入: numerator = 1, denominator = 2
输出: “0.5”
示例 2:
输入: numerator = 2, denominator = 1
输出: “2”
示例 3:
输入: numerator = 2, denominator = 3
输出: “0.(6)”
class Solution {
public:
/*
1.先将所有负数运算转化为正数运算。然后再算出分数的整数部分,再将精力集中在小数部分。
2.用一个哈希表 unordered_map<int,int> 记录所有余数所对应的商在小数点后第几位,当计算到相同的余数时,上一次余数的位置和当前位置之间的数,就是该小数的循环节。
*/
string fractionToDecimal(int numerator, int denominator) {
long long n = numerator, d = denominator;
bool minus = false; //判断有没有负号
if(n < 0) minus = !minus, n = -n;
if(d < 0) minus = !minus, d = -d;
string res = to_string(n / d);
n %= d;
if(!n)//余数为0,整数,直接输出
{
if(minus && res != "0") return '-' + res;//负数
return res;
}
res += '.';//小数点
unordered_map<long long, int> hash; //余数 和 对应的位置
while(n) //当前余数
{
if(hash[n]) //如果是当前出现过的余数 找到循环节
{
res = res.substr(0, hash[n]) + '(' + res.substr(hash[n]) + ')';
break;
}
else hash[n] = res.size();
n *= 10;
res += to_string(n / d);//商
n %= d;
}
if(minus) res = '-' + res;
return res;
}
};
LeetCode 208. Implement Trie (Prefix Tree)
实现一个 Trie (前缀树),包含 insert, search, 和 startsWith 这三个操作。
示例:
Trie trie = new Trie();
trie.insert(“apple”);
trie.search(“apple”); // 返回 true
trie.search(“app”); // 返回 false
trie.startsWith(“app”); // 返回 true
trie.insert(“app”);
trie.search(“app”); // 返回 true
说明:
你可以假设所有的输入都是由小写字母 a-z 构成的。
保证所有输入均为非空字符串。
class Trie {
public:
/** Initialize your data structure here. */
struct Node
{
bool is_end;
Node *next[26];
Node()
{
is_end = false;//标记是否有单词以此节点为结尾
for(int i = 0; i < 26; i++)
next[i] = 0;//初始化节点为 0
}
} *root;
Trie() {
root = new Node();
}
/** Inserts a word into the trie. */
void insert(string word) {
Node *p = root;
for(char c : word)
{
int son = c - 'a';//取出儿子的字母
if(!p->next[son]) p->next[son] = new Node();//没有子树 创造
p = p->next[son]; //进行下一位操作
}
p->is_end = true;//结束循环后将最后的节点标记 说明有一个单词以此节点为终点
}
/** Returns if the word is in the trie. */
bool search(string word) {
Node *p = root;
for(char c : word)
{
if(p->next[c - 'a']) p = p->next[c - 'a']; //如果当前不为空 说明存在 继续往下搜索
else
return false;
}
return p->is_end;//匹配到最后节点 且有以该节点为结尾的单词 返回true
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Node *p = root;
for(char c : prefix)
{
if(p->next[c - 'a']) p = p->next[c - 'a'];
else return false;
}
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);
*/
LeetCode 131. Palindrome Partitioning
给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。
示例:
输入: “aab”
输出:
[
[“aa”,“b”],
[“a”,“a”,“b”]
]
class Solution {
public:
/*
暴力枚举所有方案。
对字符串从前往后搜索,搜索时维护如下几个状态:
1.已经划分好的区间列表;
2.当前正在枚举的区间;
3.枚举到的字符串 s的下标;
4.字符串 s
在搜索时:分情况处理:
如果已经遍历完字符串 s,则递归终止。终止前判断方案是否合法:即判断当前区间是否是回文串;
否则,字符串 s还没遍历完,则
1.如果当前区间不是回文串,则只能继续在该区间添加字符;
2.如果当前区间是回文串,则既可以在该区间添加字符,也可以将该区间保存,并开启一个新的区间;
*/
vector<vector<string>> ans;
vector<string> path;//当前方案
vector<vector<string>> partition(string s) {
dfs("", 0, s);
return ans;
}
bool check(string &now)//检查是不是回文串
{
if(now.empty()) return false;
for(int i = 0, j = now.size() - 1; i < j; i ++, j --)
if(now[i] != now[j])
return false;
return true;
}
void dfs(string now, int u, string &s)
{
if(u == s.size())
{
if(check(now))
{
path.push_back(now);
ans.push_back(path);
path.pop_back();
}
return;
}
if(check(now))
{
path.push_back(now);
dfs("", u, s);
path.pop_back();
}
dfs(now + s[u], u + 1, s);
}
};
LeetCode 227. Basic Calculator II
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
示例 1:
输入: “3+2*2”
输出: 7
示例 2:
输入: " 3/2 "
输出: 1
示例 3:
输入: " 3+5 / 2 "
输出: 5
说明:
你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。
class Solution {
public:
int calculate(string s) {
stack<char> op;//操作符
stack<int> num;
s += "+0";
for(int i = 0; i < s.size(); i ++)
{
if(s[i] == ' ') continue;
if(s[i] == '*' || s[i] == '/' || s[i] == '+' || s[i] == '-') op.push(s[i]);
else //取出数字 可能多位
{
int j = i;
while(j < s.size() && s[j] >= '0' && s[j] <= '9') j++;
num.push(atoi(s.substr(i, j - i).c_str())); //i是起始
//atoi (表示 ascii to integer)是把字符串转换成整型数的一个函数
i = j - 1;//跳到下一个数字
if(!op.empty()) //有操作符
{
if(op.top() == '*' || op.top() == '/')
{
int y = num.top();
num.pop();
int x = num.top();
num.pop();
// x * y x / y
if(op.top() == '*') num.push(x * y);
else num.push( x / y);
op.pop();
}
else if(op.size() >= 2)
{
// x + y - z
int z = num.top();
num.pop();
int y = num.top();
num.pop();
int x = num.top();
num.pop();
char op2 = op.top();
op.pop();
char op1 = op.top();
op.pop();
if(op1 == '+') num.push(x + y), num.push(z);
else num.push( x - y), num.push(z);
op.push(op2);
}
}
}
}
num.pop();//pop掉一开始的 +0
return num.top();
}
};
LeetCode 30. Substring with Concatenation of All Words
给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:
s = “barfoothefoobarman”,
words = [“foo”,“bar”]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 “barfoor” 和 “foobar” 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:
输入:
s = “wordgoodgoodgoodbestword”,
words = [“word”,“good”,“best”,“word”]
输出:[]
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words) {
if (words.empty()) return {};
vector<int> res;
int n = s.size(), m = words.size(), w = words[0].size();
unordered_map<string, int> hashmap;
for(int i = 0; i < m; i++) hashmap[words[i]] ++;//统计每个单词出现个数
for(int i = 0; i < w; i++)
{
//[i, i + w], [i + 2, i + 2w],..., [i + (m-1)w], [i + (m-1)w, i +mw] 双指针
// i = 0, w, 2w, 3w, ...
// i = 1, w+1, ...
// ...
// i = w-1,
unordered_map<string, int> tdict; //记录上面的次数
int sum = 0;
int l = i;//起始
for(int j = l; j + w <= n; j += w)
{
string word;
if(j - m * w >= l) //**
{
word = s.substr(j - m * w, w);
if(tdict[word] == hashmap[word]) sum -= hashmap[word];
tdict[word] --;
if(tdict[word] == hashmap[word]) sum += hashmap[word];
}
word = s.substr(j, w);
if(hashmap.count(word) == 0)
{
sum = 0;
tdict.clear();
l = j + w;
}
else
{
if(tdict[word] == hashmap[word]) sum -= hashmap[word];
tdict[word] ++; //**
if(tdict[word] == hashmap[word]) sum += hashmap[word];
}
if(sum == m) res.push_back(j - (m - 1) * w); // m - 1
}
}
return res;
}
};
LeetCode 214. Shortest Palindrome
给定一个字符串 s,你可以通过在字符串前面添加字符将其转换为回文串。找到并返回可以用这种方式转换的最短回文串。
示例 1:
输入: “aacecaaa”
输出: “aaacecaaa”
示例 2:
输入: “abcd”
输出: “dcbabcd”
class Solution {
public:
string shortestPalindrome(string s) {
string ls = s;
reverse(s.begin(), s.end());
s = ls + '#' + s;
int n = s.size();
vector<int> next(n + 1, 0);
for(int i = 2, j = 0; i <= n; i ++)//KMP
{
while(j && s[j] != s[i - 1]) j = next[j];
if(s[j] == s[i - 1]) j ++;
next[i] = j;
}
int res = next[n];
string sup = ls.substr(res);
reverse(sup.begin(), sup.end());
return sup + ls;
}
};