zoukankan      html  css  js  c++  java
  • 最长字符串系列汇总

    PS: 做题时一直对几个经常常见的求最长字符串啥的混淆,例如最长回文子串,最长回文子序列,最长公共子串,最长公共子序列,无重复最长子串。。。。。。,故记录一波。 

    注意: 

    a. 子串与子序列区别在于子串是连续的,子序列可以不连续。

    b. 下面的代码侧重于用 动态规划(DP)解决,同时对特定题目也会列出其他解法。 

    -------------------------------------------------------我是帅气的分割线^^ -----------------------------------------------

    1.最长回文子串

    // 最长回文子串 (O(N^2) + O(N)) ,     // 中心扩展法
    class Solution {
    public:
        string longestPalindrome(string s) {
            string res;
            for (int i = 0; i < s.size(); i++) {
                // 以s[i] 为中心的最长回文字符串
                string s1 = ispalindrome(s, i, i);
                // 以s[i] 和 s[i+1] 为中心的最长回文字符串
                string s2 = ispalindrome(s, i, i + 1);
                res = res.size() > s1.size() ? res : s1;
                res = res.size() > s2.size() ? res : s2;
            }
            return res;
        }
            string ispalindrome(string& s, int left, int right) {
            // 防止越界
            while (left >= 0 && right < s.size() && s[left] == s[right]) {
                left--;  // 向两边展开
                right++;
            }
            // 返回以 s[left] 和 s[right] 为中心的最长回文串
            return s.substr(left + 1, right - left - 1);
        }
    };
    // 最长回文子串 (O(N^2) + O(N)) ,     // 动态规划法
    //我们将f[i][j]表述为从j到i的子串为回文串,j <= i,
    //此时dp的矩阵为左下三角!如果a[i]==a[j]且f[i-1][j+1]=true, 那么f[i][j]也为true。
    //需要注意一点:当i - j < 2时,如果s[i] = s[j],
    //那么f[i][j]必为true,即单个字符或者两个相邻相同字符为回文子串。
    class Solution11 {
    public:
            string LongPalindromeSubstr(string s) {
                   int n = s.length();
                   if (n == 0) return "";
                   string res = "";
                   vector<vector<bool>> dp(n, vector<bool>(n, false));
                   int maxlen = 0;
                   int curlen = 0;
                   for (int i = 0; i < n; i++) {
                           for (int j = 0; j <= i; j++) {   // dp[0][0]=true, 一定成立
                                  if ((s[i] == s[j]) && ((i - j < 2) || (i > 0 && dp[i - 1][j  + 1]))) {
                                          dp[i][j] = true;
                                          curlen = i - j + 1;
                                          if (curlen > maxlen) {
                                                 maxlen = curlen;
                                                 res = s.substr(j, curlen);
                                          }
                                  }
                           }
                   }
                   return res;
            }
    };

    2.最长回文子序列

    // 最长回文子序列   动态规划dp
    class Solution2 {
    public:
            int LongestpalindromeSubSequence(string s) {
                   if (s.size() == 0) return 0;
                   int n = s.size();
                   vector<vector<int>> dp(n, vector<int>(n, 0)); // dp[i][j]表示字符串 s[j:i] 的最长回文子序列长度
                   //base case
                   for (int i = 0; i < s.size(); i++) {
                           dp[i][i] = 1;
                   }
                   // 状态转移
                   for (int i = n - 1; i >= 0; i++) {  
                           for (int j = i + 1; j < n; j++) {
                                  if (s[i] == s[j]) {
                                          dp[i][j] = dp[i + 1][j - 1] + 2;
                                  }
                                  else {
                                          dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
                                  }
                           }
                   }
                   return dp[0][n - 1];  // 最长回文子串长度
            }
    };

    3.最长公共子串

    // 最长公共子串,
    class Solution3 {
    public:
            int LongestCommonSubstr(string str1, string str2) {
                   if (str1.size() == 0 && str2.size() != 0) return 0;
                   if (str1.size() != 0 && str2.size() == 0) return 0;
                   int n = str1.size();
                   int m = str2.size();
                   int res = 0;
                   vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));  // dp[i][j] 表示  str1[:i] 和 str2[:j] 的公共子串长度
                   for (int i = 0; i <= n; i++) { // base case
                           dp[i][0] = 0;
                   }
                   for (int j = 0; j <= m; j++) {
                           dp[0][j] = 0;
                   }
                   for (int i = 1; i <= n; i++) {
                           for (int j = 1; j <= m; j++) {
                                  if (str1[i - 1] == str2[j - 1]) {
                                          dp[i][j] = dp[i - 1][j - 1] + 1;
                                          res = max(dp[i][j], res); // 更新最大值
                                  }
                                  else { // 不相等的情况
                                          dp[i][j] = 0;
                                  }
                           }
                   }
                   return res;
            }
    };

    4.最长公共子序列

    // 最长公共子序列
    class Solution4 {
    public:
            int LongCommonSubSequence(string str1, string str2) {
                   int n = str1.size();  
                   int m = str2.size();
                   vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));  // dp[i][j]  代表  str1[0:i] 和 str2[0:j] 的公共子序列长度
                   for (int i = 1; i <= n; i++) {
                           for (int j = 1; j <= m; j++) {
                                  if (str1[i] == str2[j]) {
                                          dp[i][j] = dp[i - 1][j - 1] + 1;
                                  }
                                  else {
                                          dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                                  }
                           }
                   }
                   return dp[n][m];
            }
    };

    5.最长回文串(hashmap 统计字符个数)

    class Solution5 {
    public:
            int longestPalindrome(string s) {
                   if (s.size() == 0) return 0;
                   map<char, int> hashmap;
                   for (auto str : s) {
                           hashmap[str]++;
                   }
                   int count = 0;
                   for (auto it = hashmap.begin(); it != hashmap.end(); it++) {
                           if (it->second%2 == 0) {
                                  count += it->second;
                           }
                           else {
                                  count += it->second - 1;   // 如果有奇数个的,就变成偶数个的
                           }
                   }
                   if (count < s.size()) {      // 如果不是全部都是偶数个的, 则结果中只能有一个奇数个
                           count += 1;      
                   }
                   return count;
            }
    };

    6.无重复最长子串

    // 无重复最长子串  hashmap 边遍历变存储
    class Solution6 {
    public:
            int LongestNoRepeatSubstr(string str) {
                   if (str.size() == 0) return 0;
                   unordered_map<char, int> hashmap; // hashmap 记录的 字符出现的 index
                   int max_length = 0;
                   int left =g 0;
                   for (int i = 0; i < str.size(); i++) {
                           if (hashmap.find(str[i]) != hashmap.end()) {
                                  left = max(left, hashmap[str[i]] + 1); //出现重复后 更新左边界
                           }
                           max_length = max(max_length, i - left + 1);
                           hashmap[str[i]] = i;
                   }
                   return max_length;
            }
    };
    // 使用滑动窗口方法
    int lengthOfLongestSubstring(string s) {
        unordered_map<char, int> window;
        int left = 0, right = 0;
        int res = 0; // 记录结果
        while (right < s.size()) {
            char c = s[right];
            right++;
            // 进行窗口内数据的一系列更新
            window[c]++;
            // 判断左侧窗口是否要收缩
            while (window[c] > 1) {
                char d = s[left];
                left++;
                // 进行窗口内数据的一系列更新
                window[d]--;
            }
            // 在这里更新答案
            res = max(res, right - left);
        }
        return res;
    }

    7.最长上升子序列

    // 最长上升子序列  , 动态规划 O(n^2)
    class Solution7 {
    public:
            int LongestUpSubstr(vector<int> str) {
                   if (str.size() == 0) return 0;
                   int n = str.size();
                    // dp[i] 代表dp[i] 表⽰以 str[i] 这个数结尾的最⻓递增⼦序列的⻓度
                   vector<int> dp(n, 1);
                   for (int i = 0; i < n; i++) {
                           for (int j = 0; j < i; j++) {
                                  if (str[i] > str[j]) {
                                          dp[i] = max(dp[i], dp[j] + 1);
                                  }
                           }
                   }
                   int res = dp[0];
                   for (int i = 1; i < n; i++) {
                           res = max(res, dp[i]);
                   }
                   return res;
            }
    };

    这里再提供一个来自大神的优化版本,使用二分法优化

    #include <iostream>
    #include <vector>
    using namespace std;
    class Solution {
    public:
        int lengthOfLIS(vector<int> &nums) {
            int len = nums.size();
            if (len < 2) {
                return len;
            }
            vector<int> tail; //  tail 数组的定义:长度为 i + 1 的上升子序列的末尾最小是几
            tail.push_back(nums[0]);// 遍历第 1 个数,直接放在有序数组 tail 的开头
            // tail 结尾的那个索引
            int end = 0;
    
            for (int i = 1; i < len; ++i) {
                if (nums[i] > tail[end]) { //逻辑1:比 tail 数组实际有效的末尾的那个元素还大
                    tail.push_back(nums[i]); // 直接添加在那个元素的后面,所以 end 先加 1
                    end++;
                } else {
                    // 使用二分查找法,在有序数组 tail 中
                    // 找到第 1 个大于等于 nums[i] 的元素,尝试让那个元素更小
                    int left = 0;
                    int right = end;
                    while (left < right) {
                         // 选左中位数不是偶然,而是有原因的,原因请见 LeetCode 第 35 题题解
                         // int mid = left + (right - left) / 2;
                        int mid = (left + right) >> 1;  // 右移一位等于 除2了
                        if (tail[mid] < nums[i]) {
                             // 中位数肯定不是要找的数,把它写在分支的前面
                            left = mid + 1;
                        } else {
                            right = mid;
                        }
                    }
                        // 走到这里是因为 【逻辑 1】 的反面,因此一定能找到第 1 个大于等于 nums[i] 的元素
                        // 因此,无需再单独判断
                    tail[left] = nums[i];
                }
            }
              // 此时 end 是有序数组 tail 最后一个元素的索引
              // 题目要求返回的是长度,因此 +1 后返回
            return end + 1;
        }
    };
    
    作者:liweiwei1419
    链接:https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-er-fen-cha-zhao-tan-xin-suan-fa-p/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    Linux内存地址映射
    江湖行
    数组/指针/const/字符串常量的使用传值问题
    TI davinci DM6467通过串口0将UBL和u-boot写入NAND flash
    CCS5配置使用GenCodecPkg生成CODEC SERVER
    typedef用法和与define的区别
    VS2008项目移植到Linux
    bootargs中ip段各项解释
    Davinci-DM6467板子-外围器件的I2C地址的疑惑解答
    MFC中界面自适应
  • 原文地址:https://www.cnblogs.com/E-Dreamer-Blogs/p/12531945.html
Copyright © 2011-2022 走看看