2. 两数相加 - 力扣(LeetCode)
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
提示:
- 每个链表中的节点数在范围
[1, 100]
内 0 <= Node.val <= 9
- 题目数据保证列表表示的数字不含前导零
思路:
因为数字是逆序存储的,所以我们刚好可以正着加起来,我们可以先定义一个进位carry
,然后每次根据carry
和10比大小来确定当前一位的值,最后只需要注意进位为非零值的话还需要补充一个值
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* p = new ListNode();
ListNode* cur = p;
int carr = 0;
while(l1 || l2)
{
if(l1)
{
carr += l1->val;
l1 = l1->next;
}
if(l2)
{
carr += l2->val;
l2 = l2->next;
}
cur->next = new ListNode(carr % 10);
cur = cur->next;
carr = carr / 10;
}
if(carr)
cur->next = new ListNode(carr);
return p->next;
}
};
5. 最长回文子串 - 力扣(LeetCode)
给你一个字符串 s
,找到 s
中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
提示:
1 <= s.length <= 1000
s
仅由数字和英文字母组成
思路:
回文子串问题很多都可以考虑区间dp来实现,我们定义 d p [ l ] [ r ] dp[l][r] dp[l][r]表示字符串中 l l l到 r r r的区间是否回文,所以我们就可以得到dp的表达式如下
- 如果区间长度为 l e n len len的话, d p [ l ] [ r ] = 1 dp[l][r] = 1 dp[l][r]=1;
- 如果区间长度为2,并且 s [ r ] = = s [ l ] s[r] == s[l] s[r]==s[l],则 d p [ l ] [ r ] = 1 dp[l][r] = 1 dp[l][r]=1;
- 其他的情况 d p [ l ] [ r ] = d p [ l + 1 ] [ r − 1 ] & & ( s [ r ] = = s [ l ] ) dp[l][r]=dp[l + 1][r - 1]\&\&(s[r] == s[l]) dp[l][r]=dp[l+1][r−1]&&(s[r]==s[l]);
class Solution {
public:
bool dp[1010][1010];
string longestPalindrome(string s) {
int n = s.size();
memset(dp, 0, sizeof dp);
int length = 0, start = 0;
for(int len = 1; len <= n; len ++)
{
for(int i = 0; i + len - 1 < n; i ++)
{
int l = i, r = i + len - 1;
if(len == 1) dp[l][r] = true;
else if(len == 2) dp[l][r] = s[l] == s[r];
else dp[l][r] = dp[l + 1][r - 1] && (s[r] == s[l]);
if(dp[l][r] && len >= length)
{
length = len;
start = l;
}
}
}
return s.substr(start, length);
}
};
类似的题目:647. 回文子串 - 力扣(LeetCode)
附上答案
class Solution {
public:
static const int N = 1010;
bool dp[N][N];
int countSubstrings(string s) {
int n = s.size();
memset(dp, false, sizeof dp);
int start = 0, length = 0;
int ans = 0;
for(int len = 1; len <= n; len ++)
{
for(int i = 0; i + len - 1 < n; i ++)
{
int l = i, r = i + len - 1;
if(len == 1) dp[i][i] = true;
else if(len == 2) dp[l][r] = s[l] == s[r];
else dp[l][r] = dp[l + 1][r - 1] && (s[r] == s[l]);
ans += dp[l][r];
}
}
return ans;
}
};
10. 正则表达式匹配 - 力扣(LeetCode)
给你一个字符串 s
和一个字符规律 p
,请你来实现一个支持 '.'
和 '*'
的正则表达式匹配。
'.'
匹配任意单个字符'*'
匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s
的,而不是部分字符串。
示例 1:
输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
提示:
1 <= s.length <= 20
1 <= p.length <= 20
s
只包含从a-z
的小写字母。p
只包含从a-z
的小写字母,以及字符.
和*
。- 保证每次出现字符
*
时,前面都匹配到有效的字符
思路:
设状态
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示字符串s
的前i
个字符和字符串p
的前j
个字符是否能够匹配,我们可以得到一下两种转移关系
- 平凡转移 f ( i , j ) = f ( i , j ) ∣ ( f ( i − 1 , j − 1 ) ,当 i > 0 且 s ( i ) = = p ( j ) 或 p ( j ) = = ′ . ′ ) f(i,j) = f(i,j) | (f(i-1,j-1) \text{,当} i > 0 \text{且} s(i) == p(j) \text{或} p(j) == '.') f(i,j)=f(i,j)∣(f(i−1,j−1),当i>0且s(i)==p(j)或p(j)==′.′)。
- 当 p ( j ) = = ′ ∗ ′ p(j) == '*' p(j)==′∗′ 时,若 j ≥ 2 j \geq 2 j≥2, f ( i , j ) f(i,j) f(i,j) 可以从 f ( i , j − 2 ) f(i,j-2) f(i,j−2) 转移,表示丢弃这一次的 ‘’ 和它之前的那个字符;若 i > 0 i > 0 i>0 且 s ( i ) = = p ( j − 1 ) s(i) == p(j - 1) s(i)==p(j−1),表示这个字符可以利用这个 '’,则可以从 f ( i − 1 , j − 1 ) f(i-1,j - 1) f(i−1,j−1) 转移,表示利用 ‘*’。
class Solution {
public:
bool isMatch(string s, string p) {
int n = s.length(), m = p.length();
vector<vector<bool>> f(n + 1, vector<bool>(m + 1, false));
s = " " + s;
p = " " + p;
f[0][0] = true;
for (int i = 0; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (i > 0 && (s[i] == p[j] || p[j] == '.'))
f[i][j] = f[i][j] | f[i - 1][j - 1];
if (p[j] == '*') {
if (j >= 2)
f[i][j] = f[i][j] | f[i][j - 2];
if (i > 0 && (s[i] == p[j - 1] || p[j - 1] == '.'))
f[i][j] = f[i][j] | f[i - 1][j - 1];
}
}
return f[n][m];
}
};
11. 盛最多水的容器 - 力扣(LeetCode)
给定一个长度为 n
的整数数组 height
。有 n
条垂线,第 i
条线的两个端点是 (i, 0)
和 (i, height[i])
。
找出其中的两条线,使得它们与 x
轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
**说明:**你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104
思路:
这道题是贪心思路,因为我们定义两个指针从两边往中间指,如果左指针>右指针,则右指针往左移动,反之一样
class Solution {
public:
int maxArea(vector<int>& height) {
int ans = 0, left = 0, right = height.size() - 1;
while(left < right)
{
int area = (right - left) * min(height[left], height[right]);
ans = max(ans, area);
height[left] < height[right] ? left ++ : right --;
}
return ans;
}
};
15. 三数之和 - 力扣(LeetCode)
给你一个整数数组 nums
,判断是否存在三元组 [nums[i], nums[j], nums[k]]
满足 i != j
、i != k
且 j != k
,同时还满足 nums[i] + nums[j] + nums[k] == 0
。请
你返回所有和为 0
且不重复的三元组。
**注意:**答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105
思路:
运用双指针,每次枚举中间的数,两个指针分别从两边往中间靠拢,这样就可以把时间复杂度优化到 O ( n 2 ) O(n^2) O(n2)了
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(), nums.end());
int n = nums.size();
for(int i = 0; i < n - 2; i ++)
{
int x = nums[i];
if(i && x == nums[i - 1])
continue;
int j = i + 1, k = n - 1;
while(j < k)
{
if(x + nums[j] + nums[k] > 0)
k --;
else if(x + nums[j] + nums[k] < 0)
j ++;
else
{
res.push_back({x, nums[j], nums[k]});
j += 1;
while(j < k && nums[j] == nums[j - 1])
j ++;
k -= 1;
while(k > j && nums[k] == nums[k + 1])
k --;
}
}
}
return res;
}
};
17. 电话号码的字母组合 - 力扣(LeetCode)
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23"
输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = ""
输出:[]
示例 3:
输入:digits = "2"
输出:["a","b","c"]
提示:
0 <= digits.length <= 4
digits[i]
是范围['2', '9']
的一个数字。
思路:
这道题就是经典的dfs
问题,遵循最基本的dfs
模板即可,需要注意到是要将数字和字符对应起来,具体可以看一下代码注释
class Solution {
public:
string mapping[10] = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
string t;
vector<string> ans;
void dfs(string num, int index)
{
if(index == num.size())
{
ans.push_back(t);
return;
}
int digit = num[index] - '0';
string s = mapping[digit];
for(int i = 0; i < s.size(); i ++)
{
t += s[i];
dfs(num, index + 1);
t.pop_back();
}
}
vector<string> letterCombinations(string digits) {
if(digits.empty())
return ans;
dfs(digits, 0);
return ans;
}
};