目录
- [19. 在字符串中找出连续最长的数字串(模拟 + 双指针)](#19. 在字符串中找出连续最长的数字串(模拟 + 双指针))
- [20. 岛屿数量(BFS / DFS)](#20. 岛屿数量(BFS / DFS))
- [21. 拼三角(枚举 / dfs)](#21. 拼三角(枚举 / dfs))
- [22. 求最小公倍数(数学)](#22. 求最小公倍数(数学))
- [23. 数组中的最长连续子序列(排序 + 模拟)](#23. 数组中的最长连续子序列(排序 + 模拟))
- [24. 字母收集(动态规划 - 路径问题)](#24. 字母收集(动态规划 - 路径问题))
- [25. 添加逗号(模拟)](#25. 添加逗号(模拟))
- [26. 跳台阶(动态规划)](#26. 跳台阶(动态规划))
- [27. 扑克牌顺子(排序)](#27. 扑克牌顺子(排序))
- [28. 最长回文子串(回文串)](#28. 最长回文子串(回文串))
- [29. 买卖股票的最好时机(一)(贪心)](#29. 买卖股票的最好时机(一)(贪心))
- [30. 过河卒(动态规划 - 路径问题)](#30. 过河卒(动态规划 - 路径问题))
- [31. 游游的水果大礼包(枚举)](#31. 游游的水果大礼包(枚举))
- [32. 买卖股票的最好时机(二)(贪心)](#32. 买卖股票的最好时机(二)(贪心))
- [33. 倒置字符串(字符串)](#33. 倒置字符串(字符串))
- [34. 删除公共字符(哈希)](#34. 删除公共字符(哈希))
- [35. 两个链表的第一个公共结点(链表)](#35. 两个链表的第一个公共结点(链表))
- [36. mari和shiny(动态规划 - 线性dp)](#36. mari和shiny(动态规划 - 线性dp))
19. 在字符串中找出连续最长的数字串(模拟 + 双指针)

遍历整个字符串,遇到数字的时候,用双指针找出这段连续的数字子串,根据此时的长度更新起始位置和长度。
cpp
#include<iostream>
#include<string>
#include<cctype>
using namespace std;
int main() {
string s;
cin >> s;
int maxi = 0, maxlen = 0;
int i = 0, prev = -1;
while (i < s.size()) {
if (isdigit(s[i])) {
if (i - prev > maxlen) {
maxi = prev + 1;
maxlen = i - prev;
}
} else {
prev = i;
}
i++;
}
cout << s.substr(maxi, maxlen) << endl;
return 0;
}
20. 岛屿数量(BFS / DFS)

经典的 floodfill 算法。用 dfs 或者是 bfs 找出一个联通的区域,并且标记上。看看一共能找出几个联通块。
cpp
class Solution {
public:
int m, n;
bool vis[201][201];
int solve(vector<vector<char> >& grid) {
memset(vis, false, sizeof vis);
m = grid.size();
n = grid[0].size();
int ret = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '1' && !vis[i][j]) {
ret++;
bfs(grid, i, j);
}
}
}
return ret;
}
int dx[4] = {1, -1, 0, 0};
int dy[4] = {0, 0, 1, -1};
void bfs(vector<vector<char> >& grid, int i, int j) {
queue<pair<int, int>> q;
vis[i][j] = true;
q.push({i, j});
while (!q.empty()) {
auto [a, b] = q.front();
q.pop();
for (int k = 0; k < 4; k++) {
int x = a + dx[k];
int y = b + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '1' && !vis[x][y]) {
vis[x][y] = true;
q.push({x, y});
}
}
}
}
};
21. 拼三角(枚举 / dfs)

因为数据量很小,这道题可以直接暴力枚举
cpp
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
bool check(vector<int>& v)
{
if(v[5] < v[0] + v[1] && v[4] < v[2] + v[3]
|| v[5] < v[0] + v[2] && v[4] < v[1] + v[3]
|| v[5] < v[0] + v[3] && v[4] < v[2] + v[1]
|| v[5] < v[0] + v[4] && v[3] < v[2] + v[1]
|| v[5] < v[1] + v[2] && v[4] < v[0] + v[3]
|| v[5] < v[1] + v[3] && v[4] < v[2] + v[0]
|| v[5] < v[1] + v[4] && v[3] < v[2] + v[0]
|| v[5] < v[2] + v[3] && v[4] < v[1] + v[0]
|| v[5] < v[2] + v[4] && v[3] < v[1] + v[0]
|| v[5] < v[3] + v[4] && v[2] < v[0] + v[1])
return true;
return false;
}
int main()
{
int t;
cin >> t;
while(t--)
{
vector<int> v(6);
for(int i = 0; i < 6; i++)
{
cin >> v[i];
}
sort(v.begin(), v.end());
cout << (check(v)?"Yes":"No") << endl;
}
return 0;
}
22. 求最小公倍数(数学)
cpp
#include<iostream>
using namespace std;
int main()
{
int a, b;
cin >> a >> b;
int start = min(a, b), end = a*b;
for(int n = start; n <= end; n += start)
{
if(n % a == 0 && n % b == 0)
{
cout << n << endl;
break;
}
}
return 0;
}
23. 数组中的最长连续子序列(排序 + 模拟)

一般方法是排序+模拟,时间复杂度O(nlogn)
cpp
class Solution {
public:
int MLS(vector<int>& arr)
{
sort(arr.begin(), arr.end());
int i = 0;
int ret = 1;
int tmp = 1;
while(i < arr.size()-1)
{
if(arr[i+1] == arr[i] + 1)
{
tmp++;
ret = max(ret, tmp);
i++;
}
else if(arr[i+1] == arr[i])
{
i++;
}
else
{
tmp = 1;
i++;
}
}
return ret;
}
};
也可以利用并查集的思想,哈希表映射每个数和他所处连续序列的最长长度,时间复杂度O(n)完成
cpp
class Solution
{
public:
int MLS(vector<int>& arr) {
unordered_map<int, int> len;
int ret = 0;
for(int n : arr)
{
if(len.count(n) == 0)
{
int left = len.count(n-1)==0? 0: len[n-1];
int right = len.count(n+1)==0? 0: len[n+1];
int curlen = left + right + 1;
ret = max(ret, curlen);
len[n] = len[n-left] = len[n+right] = curlen;
}
}
return ret;
}
};
24. 字母收集(动态规划 - 路径问题)

基础的路径问题的 dp 模型。
cpp
#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<char>> vv(n, vector<char>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> vv[i][j];
}
}
int hash[26];
memset(hash, 0, sizeof hash);
hash['l' - 'a'] = 4;
hash['o' - 'a'] = 3;
hash['v' - 'a'] = 2;
hash['e' - 'a'] = 1;
//dp[i][j]表示达到当前位置时最多分
vector<vector<int>> dp(n, vector<int>(m));
dp[0][0] = hash[vv[0][0] - 'a'];
for (int i = 1; i < n; i++)
{
dp[i][0] = dp[i - 1][0] + hash[vv[i][0] - 'a'];
}
for (int j = 1; j < m; j++) {
dp[0][j] = dp[0][j - 1] + hash[vv[0][j] - 'a'];
}
for (int i = 1; i < n; i++) {
for (int j = 1; j < m; j++) {
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + hash[vv[i][j] - 'a'];
}
}
cout << dp[n - 1][m - 1] << endl;
return 0;
}
25. 添加逗号(模拟)

从后往前遍历这个数,每提取三个数字的时候,加一个逗号。最后处理一下边界情况即可。
cpp
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int main()
{
long long n;
cin >> n;
string s;
int count = 1;
while(n)
{
s += n%10+'0';
n /= 10;
if(count++ % 3 == 0)
{
s += ',';
}
}
if(s.back() == ',')
{
s.pop_back();
}
reverse(s.begin(), s.end());
cout << s << endl;
return 0;
}
26. 跳台阶(动态规划)

入门级的动态规划问题
cpp
#include<iostream>
using namespace std;
int main()
{
int n;
cin >> n;
int dp[41];
dp[0] = 1, dp[1] = 1;
for(int i = 2; i <= n; i++)
{
dp[i] = dp[i-1] + dp[i-2];
}
cout << dp[n] << endl;
return 0;
}
27. 扑克牌顺子(排序)

如果能够构成顺子的话,所有的非零元素应该满足下面两个条件:
- 不能出现重复元素;
- max - min <= 4
cpp
class Solution {
bool hash[14] = { 0 };
public:
bool IsContinuous(vector<int>& numbers) {
int maxVal = 0, minVal = 14;
for (auto x : numbers) {
if (x) {
if (hash[x]) return false;
hash[x] = true;
maxVal = max(maxVal, x);
minVal = min(minVal, x);
}
}
return maxVal - minVal <= 4;
}
};
28. 最长回文子串(回文串)

枚举所有的中心点,然后向两边扩散。
对于每一个点,都可能有两种情况:bab或aa,需要判断两次。
cpp
class Solution {
public:
int getLongestPalindrome(string A)
{
int ret = 1;
for(int i = 0; i < A.size(); i++)
{
int l = i-1, r = i+1;
while(l >= 0 && r < A.size() && A[l] == A[r])
{
ret = max(ret, r-l+1);
l--;
r++;
}
l = i, r = i+1;
while(l >= 0 && r < A.size() && A[l] == A[r])
{
ret = max(ret, r-l+1);
l--;
r++;
}
}
return ret;
}
};
29. 买卖股票的最好时机(一)(贪心)

因为只能买卖一次,因此,对于第 i 天来说,如果在这天选择卖出股票,应该在 [0, i] 天之
内,股票最低点买入股票,此时就可以获得第 i 天的最大利润。
cpp
#include <algorithm>
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> p(n);
for(int i = 0; i < n; i++)
{
cin >> p[i];
}
// dp[n]表示第n天卖股票的最大利润
// lowp[n]表示n天前(不包括n天)的最低价格股票
vector<int> dp(n);
vector<int> lowp(n);
dp[0] = 0, lowp[0] = p[0];
for(int i = 1; i < n; i++)
{
lowp[i] = min(lowp[i-1], p[i-1]);
dp[i] = p[i] - lowp[i];
}
cout << *max_element(dp.begin(), dp.end()) << endl;
return 0;
}
30. 过河卒(动态规划 - 路径问题)

简单路径 dp 问题:相当于是有障碍物的路径类问题,标记走到障碍物上的方法数为 0 即可。
cpp
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n, m, x, y;
cin >> n >> m >> x >> y;
n++;
m++;
vector<vector<bool>> horse(n, vector<bool>(m, false));
int dx[] = {0, 1, 2, 2, 1, -1, -1, -2, -2};
int dy[] = {0, 2, 1, -1, -2, 2, -2, 1, -1};
for(int k = 0; k < 9; k++)
{
int a = x + dx[k];
int b = y + dy[k];
if(a >= 0 && a < n && b >= 0 && b < m)
{
horse[a][b] = true;
}
}
//int会卡
vector<vector<long long>> dp(n, vector<long long>(m, 0));
if(horse[0][0])
{
cout << 0 << endl;
return 0;
}
dp[0][0] = 1;
for(int i = 1; i < n; i++)
{
dp[i][0] = horse[i][0]? 0: dp[i-1][0];
}
for(int j = 1; j < m; j++)
{
dp[0][j] = horse[0][j]? 0: dp[0][j-1];
}
for(int i = 1; i < n; i++)
{
for(int j = 1; j < m; j++)
{
dp[i][j] = horse[i][j]? 0: dp[i-1][j]+dp[i][j-1];
}
}
cout << dp[n-1][m-1] << endl;
return 0;
}
31. 游游的水果大礼包(枚举)

很容易想到贪心,但是很不幸,贪心是错的。
正确的解法应该是枚举所有的情况。
cpp
#include <iostream>
using namespace std;
long long n, m, a, b;
int main()
{
cin >> n >> m >> a >> b;
long long ret = 0;
for(long long x = 0; x <= min(n / 2, m); x++) // 枚举 1 号礼包的个数
{
long long y = min(n - x * 2, (m - x) / 2); // 计算 2 号礼包的个数
ret = max(ret, a * x + b * y);
}
cout << ret << endl;
return 0;
}
32. 买卖股票的最好时机(二)(贪心)

简单多状态dp
cpp
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int n;
cin >> n;
vector<int> p(n);
for(int i = 0; i < n; i++)
{
cin >> p[i];
}
vector<vector<int>> dp(n, vector<int>(2));
// dp[i][0]为第i天手上没股票的最大利润, dp[i][1]为第i天手上有股票的最大利润
dp[0][0] = 0, dp[0][1] = -p[0];
for(int i = 1; i < n; i++)
{
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+p[i]);
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-p[i]);
}
cout << max(dp[n-1][0], dp[n-1][1]) << endl;
return 0;
}
33. 倒置字符串(字符串)

cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string ret;
string s;
while(cin >> s)
{
ret = s + ' ' + ret;
}
ret.pop_back();
cout << ret << endl;
return 0;
}
34. 删除公共字符(哈希)

用哈希表记录一下待删除的字符信息即可。
cpp
#include <iostream>
#include <string>
#include <unordered_set>
using namespace std;
int main()
{
string s1, s2;
getline(cin, s1);
getline(cin, s2);
unordered_set<char> hash;
for(char ch : s2)
hash.insert(ch);
string ret;
for(char ch : s1)
{
if(hash.count(ch) == 0)
{
ret += ch;
}
}
cout << ret << endl;
return 0;
}
35. 两个链表的第一个公共结点(链表)

链表很经典的题目,让较长链表提前走两个链表长度之差步,再让两个链表同时往后走。
cpp
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};*/
class Solution {
public:
ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2)
{
int len1 = 0, len2 = 0;
ListNode* cur1 = pHead1;
ListNode* cur2 = pHead2;
while(cur1)
{
len1++;
cur1 = cur1->next;
}
while(cur2)
{
len2++;
cur2 = cur2->next;
}
cur1 = pHead1;
cur2 = pHead2;
if(len1 > len2)
{
for(int i = 0; i < len1-len2; i++)
{
cur1 = cur1->next;
}
}
else
{
for(int i = 0; i < len2-len1; i++)
{
cur2 = cur2->next;
}
}
while(cur1 && cur2)
{
if(cur1 == cur2)
{
return cur1;
}
cur1 = cur1->next;
cur2 = cur2->next;
}
return nullptr;
}
};
36. mari和shiny(动态规划 - 线性dp)

简单线性 dp:
维护 i 位置之前,一共有多少个 "s" "sh" ,然后更新 "shy" 的个数。
cpp
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n;
cin >> n;
string str;
cin >> str;
long long s = 0, sh = 0, shy = 0;
for(char ch : str)
{
if(ch == 's')
{
s++;
}
else if(ch == 'h')
{
sh = sh + s;
}
else if(ch == 'y')
{
shy = shy + sh;
}
}
cout << shy << endl;
return 0;
}
