剑指Offer刷题记录

剑指Offer刷题记录,第1张

剑指Offer刷题记录 动态规划(中等) Q1把数字翻译成字符

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

我的第一思路是利用递归思想,将问题根据数字数组的长度和前两个数字来划分成子问题:

当长度为0或者1时,递归函数直接返回1,因为此时不会有多余情况了当长度大于2时,要根据前两位数进行划分:

    若第一个数字为0或者二位数大于25,则此时只有一种情况,即将第一个数字翻译成一个字母,返回后参数是 后n-1个数字串 可能的情况类型个数若二位数<25 ,则可以有两种划分 ,即两个数字作为一个字母,或者一个对应一个字母

显然,递归算法会开辟更多的空间,但时间复杂度较好:

class Solution {
public:
    int translateNum(int num) {
 
      vector nums;
      int n=0;
      while(num)
      {
         nums.insert(nums.begin(),num - (num/10)*10);
         num = num/10;
         n++;
      } 
      return countNum(nums,0,n);     
    }
    int countNum(vector nums,int a , int n)
      { 
          if(n < 0) return 0;
          else if(n == 1 || n == 0) return 1;
          else {
            if(nums[a]*10+nums[a+1] > 25 || nums[a]==0 ) return  countNum(nums,a+1,n-1);
            else return   countNum(nums,a+1,n-1) + countNum(nums,a+2,n-2);
          };
      }
};

本题最好的方法还是动态规划的方法,主要思想如下:

dp[ i ] 表示以i为结尾的数字串的翻译类型个数

转移方程: d p[ i ] = if (i 和 i-1 能组合翻译) dp[ i ] = dp[i-1] + dp[ i - 2]

​ else : dp[i] = dp [ i-1]

提升性能的两个tips:

    由于整体顺序是一遍的遍历,因此可以不需要先将数转换为字符串或者一维数组,直接循环取余,自右向左动态规划由于每次运算只涉及dp数组中的第 i i -1 i -2 的元素,因此也不需要开辟dp数组,直接用a b c变量进行代替即可
class Solution {
public:
    int translateNum(int num) {
        string str = to_string(num);
        int len = str.size();
        if(len < 2) return len;
        int a,b,c;             //代表dp i-2/i-1/i
        b = 1;
        a = 1;
        for(int i = 2;i <= len;i++){
            if(str[i-2] == '1' || (str[i-2] == '2' && str[i-1] <= '5')) c = a+b;
            else c = b;
        }
        return dp[len];
    }
};
Q2 最长不含重复字符的子字符串

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

我的第一种思路,使用集合的数据结构,根据字符串进行递推,dp[ i ]表示以 i 为结尾的最长的不重复子字符串:

当某一字符在当前字符集合已经存在,则清空集合,回溯,构造新的集合,计算dp值当某一字符不存在与当前集合,则加入集合,dp[ i ] = dp[ i-1 ] + 1

缺点:dp和集合 占用空间,回溯浪费大量时间

class Solution {
public:
    int lengthOfLongestSubstring(string s) {

        set sett;
        int n = s.length();
        if(n <= 1) return n;
        int dp[n];
        dp[0] = 1;
        sett.insert(s[0]);
        int max = 1,j;
        for(int i=1;i=0;j--)
                 {
                     sett.insert(s[j]);
                     if(s[i] == s[j]) break;
                 }
                   dp[i] = i - j;               
             }else{
                 dp[i] = dp[i-1] + 1;
                 sett.insert(s[i]);
                 if(dp[i]>max) max = dp[i];
             }
          }     
        return max;
    }
};

根据题解,其实不需要使用集合,在第一种情况中,只需要向左找到第一个相同的元素求差即可,当然也可以用哈希表来存贮有关信息,减少了向左搜索的过程。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int len = s.size();
        if (len == 0) {
            return 0;
        }

        unordered_map hash;          //存储每个字符 和 该字符最后出现的位置(每次遍历到相同的字符会更新下标)
        int max_len = 0, sub_len = 0;        
        for(int right=0;right					
										


					

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5718683.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存