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)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 相关阅读:
    hdoj2187:悼念512汶川大地震遇难同胞 (贪心)
    2.0其它之Transform详解,以及UIElement和FrameworkElement的常用属性
    2.0外观之样式, 模板, 视觉状态和视觉状态管理器
    2.0图形之Ellipse, Line, Path, Polygon, Polyline, Rectangle
    2.0控件之ListBox, MediaElement, MultiScaleImage, PasswordBox, ProgressBar, RadioButton
    2.0画笔之SolidColorBrush, ImageBrush, VideoBrush, LinearGradientBrush, RadialGradientBrush
    2.0图形之基类System.Windows.Shapes.Shape
    2.0交互之鼠标事件和键盘事件
    2.0控件之ScrollViewer, Slider, StackPanel, TabControl, TextBlock, TextBox, ToggleButton
    2.0交互之InkPresenter(涂鸦板)
  • 原文地址:https://www.cnblogs.com/E-Dreamer-Blogs/p/12531945.html
Copyright © 2011-2022 走看看