把数字翻译成字符串

剑指 Offer 46. 把数字翻译成字符串 - 力扣(Leetcode)

题目要求

给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。

例子:

1
2
3
输入: 12258
输出: 5
解释: 122585种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi""mzi"

提示:

  • 0 <= num < 231

思路分析

动态规划

  1. dp[i]含义:长度位i时一共有dp[i]种翻译方案;
  2. 求关系式:
    • dp[i] = dp[i-1] + dp[i-2]; (10<= temp <= 25)
    • dp[i] = dp[i-1];
  3. 初始值:dp[0] = 1 dp[1] = 1;

不使用dp数组:通过斐波那契数列:f(n) = f(n-1) + f(n-2);

代码实现

动态规划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public int translateNum(int num) {
// 特殊情况
if(num <= 9){
return 1;
}
// 把num转换位char数组
char[] arr = String.valueOf(num).toCharArray();
int len = arr.length; // 数组长度
// 定义dp数组
int[] dp = new int[len+1];

// 初始值
dp[0] = 1;
dp[1] = 1;

for(int i=2;i<=len;i++){
// 计算i和i-1个字符的组合
int temp = 10 * (arr[i-2] - '0') + (arr[i-1] - '0');
if(temp>=10 && temp <= 25){
dp[i] = dp[i-1] + dp[i-2];
}else{
dp[i] = dp[i-1];
}
}
return dp[len];
}
}

动态规划+不使用dp数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public int translateNum(int num) {
// 特殊情况
if(num <= 9){
return 1;
}
// 把num转换位char数组
char[] arr = String.valueOf(num).toCharArray();
int len = arr.length; // 数组长度
// 斐波那契数列:f(n) = f(n-1) + f(n-2)
// 使用 a b c 存放
// 初始值
int a = 1,b = 1,c = 1;

for(int i=2;i<=len;i++){
// 计算i和i-1个字符的组合
int temp = 10 * (arr[i-2] - '0') + (arr[i-1] - '0');
if(temp>=10 && temp <= 25){
c = a + b;
}else{
c = b;
}
// 进行迭代
a = b;
b = c;
}
return c;
}
}

礼物的最大价值

剑指 Offer 47. 礼物的最大价值 - 力扣(Leetcode)

题目要求

在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?

例子:

1
2
3
4
5
6
7
8
输入: 
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 12
解释: 路径 13521 可以拿到最多价值的礼物

提示:

  • 0 < grid.length <= 200
  • 0 < grid[0].length <= 200

思路分析

动态规划

  1. dp[i][j] 的含义:当走到ij这个位置时,礼物最大价值位dp[i][j];

  2. 关系式:dp[i][j] = max(dp[i-1][j] ,dp[i][j-1]) + grid[i][j];

  3. 初始值:

    • dp[0][0] = grid[0][0];

    • for循环遍历:

      • dp[i][0] = dp[i-1][0] + grid[i][0];

      • ``dp[0][j] = dp[0][j-1] + grid[0][j];`

拓展:将二维数组转换位一维数组,一行一样来计算;

代码实现

动态规划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int maxValue(int[][] grid) {
// 获取数组长度
int m = grid.length;
int n = grid[0].length;
// 定义dp数组
int[][] dp = new int[m][n];
dp[0][0] = grid[0][0];
for(int i=1;i<m;i++){
dp[i][0] = dp[i-1][0] + grid[i][0];
}
for(int j=1;j<n;j++){
dp[0][j] = dp[0][j-1] + grid[0][j];
}
// 计算二维dp数组
for(int i=1;i<m;i++){
for(int j=1;j<n;j++){
dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[m-1][n-1];
}
}

拓展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public int maxValue(int[][] grid) {
// 获取数组长度
int m = grid.length;
int n = grid[0].length;
// 定义dp数组
int[] dp = new int[n];
dp[0] = grid[0][0];

for(int j=1;j<n;j++){
dp[j] = dp[j-1] + grid[0][j];
}
// 计算dp数组
for(int i=1;i<m;i++){
dp[0] = dp[0] + grid[i][0];
for(int j=1;j<n;j++){
dp[j] = Math.max(dp[j],dp[j-1])+grid[i][j];
}
}
return dp[n-1];
}
}

最长不含重复字符的子字符串

剑指 Offer 48. 最长不含重复字符的子字符串 - 力扣(Leetcode)

题目要求

请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3

输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1

输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

提示:

  • s.length <= 40000

思路分析

动态规划

  1. dp数组含义:当字符串s[i]结尾时,不含重复字符的子字符串长度为dp[i]

  2. 关系式:

    是否在区间出现,假设出现过且下标为k;

    • dp[i] = dp[i-1] +1; (i-k > dp[i-1])
    • dp[i] = i - k; (i-k <= dp[i-1])
  3. 初始值:dp[0] = 1;

拓展:dp数组可以优化为单个变量,因为我们只用到了dp[i-1],例如dp[i-2],dp[i-3]都没有使用,产生了浪费;

代码实现

动态规划

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public int lengthOfLongestSubstring(String s) {
// 特殊情况
if(s == null || s.length() <= 0){
return 0;
}
// 定义hashMap
Map<Character,Integer> map = new HashMap<>();
// 定义dp数组
int[] dp = new int[s.length()];
int res = 1; // 来记录最大值
// 初始化
dp[0] = 1;
map.put(s.charAt(0),0);
// 循环
for(int i=1;i<s.length();i++){
if(!map.containsKey(s.charAt(i))){
dp[i] = dp[i-1] +1;
}else{
// 在区间内,获取下标
int k = map.get(s.charAt(i));
// 是否在区间内
dp[i] = i - k <= dp[i-1] ? i-k : dp[i-1] + 1;
}
res = Math.max(res,dp[i]);// 记录最大值
map.put(s.charAt(i),i);// 更新索引
}
return res;
}
}

拓展

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public int lengthOfLongestSubstring(String s) {
// 特殊情况
if(s == null || s.length() <= 0){
return 0;
}
// 定义hashMap
Map<Character,Integer> map = new HashMap<>();
// 定义变量替换dp数组
int dp = 1;
int res = 1; // 来记录最大值
// 初始化
map.put(s.charAt(0),0);
// 循环
// 没有刷新之前a表示dp[i-1],刷新后a表示dp[i]
for(int i=1;i<s.length();i++){
if(!map.containsKey(s.charAt(i))){
dp = dp+1;
}else{
// 在区间内,获取下标
int k = map.get(s.charAt(i));
// 是否在区间内
dp= i - k <= dp? i-k : dp+ 1;
}
res = Math.max(res,dp);// 记录最大值
map.put(s.charAt(i),i);// 更新索引
}
return res;
}
}

丑数

剑指 Offer 49. 丑数 - 力扣(Leetcode)

题目要求

我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。

例子:

1
2
3
输入: n = 10
输出: 12
解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。

提示:

  1. 1 是丑数。
  2. n 不超过1690。

思路分析

动态规划

  1. dp数组含义:dp[i]表示第i个丑数;
  2. 关系式:dp[i] = min(dp[a]x2,dp[b]x3,dp[c]x5);
  3. 初始值:dp[1] = 1;

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution {
public int nthUglyNumber(int n) {
int a=1,b=1,c=1;
// 定义dp数组
int[] dp = new int[n+1];
// 初始值
dp[1] = 1;
// 循环
for(int i=2;i<=n;i++){
dp[i] = Math.min(Math.min(dp[a]*2,dp[b]*3),dp[c]*5);
if(dp[i] == dp[a]*2) a++;
if(dp[i] == dp[b]*3) b++;
if(dp[i] == dp[c]*5) c++;
}
return dp[n];
}
}

第一个只出现一次的字符

剑指 Offer 50. 第一个只出现一次的字符 - 力扣(Leetcode)

题目要求

在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。

例子:

1
2
3
4
5
输入:s = "abaccdeff"
输出:'b'

输入:s = ""
输出:' '

提示:

0 <= s 的长度 <= 50000

思路分析

有序哈希表

通过遍历所给字符串,使用哈希表存放字符以及使用boolean值标记它出现的情况(因为只要出现次数超过1次以上就不符合条件了,因此使用boolean进行标记比使用Integer更合理且节省空间,还能简化代码逻辑),最后将第一个出现且只出现一次的返回即可;

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public char firstUniqChar(String s) {
// 特殊情况
if(s==null || s.length() <=0){
return ' ';
}
// 定义一个有序哈希表 其中false代表不符合条件
Map<Character,Boolean> map = new LinkedHashMap<>();
for(int i=0;i<s.length();i++){
// 这里我们取反,如果字符原本不存在,就会返回false,取反之后为true
map.put(s.charAt(i),!map.containsKey(s.charAt(i)));
}
// 遍历结果
for(Map.Entry<Character,Boolean> m:map.entrySet()){
if(m.getValue()){
return m.getKey();
}
}
return ' ';
}
}