子串、子序列问题

子串、子序列问题

字符串

最长公共子序列

dp[i] [j] 表示以下标i结尾的str1 和 以下标j结尾的str2的最长公共子序列的长度。

dp[i] [j]=

  1. 0, i=0或者j=0
  2. dp[i-1] [j-1]+1, str[i-1]==str[j-1]
  3. max( dp[i-1] [j], dp[i] [j-1] ), str[i-1]!=str[j-1]
public int longestCommonSubsequence(String text1, String text2) {
    if (text1 == null || text2 == null) {
        return 0;
    }
    int len1 = text1.length();
    int len2 = text2.length() ;
    int[][] dp = new int[len1+1][len2+1];
    for (int i = 1; i <=len1; i++) {
        for (int j = 1; j <=len2; j++) {
            if(text1.charAt(i-1)==text2.charAt(j-1)){
                dp[i][j]=dp[i-1][j-1]+1;
            }else {
                dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    return dp[len1][len2];

}

最长公共子串

dp[i] [j] 表示以下标i结尾的str1 和 以下标j结尾的str2的最长公共子串的长度。

dp[i] [j]=

  1. 0, i=0或者j=0
  2. dp[i-1] [j-1]+1, str1[i-1]==str2[j-1]
  3. 0, str1[i-1]!=str2[j-1]
public int longestCommonSubStr(String text1, String text2) {
    if (text1 == null || text2 == null) {
        return 0;
    }
    int res = 0;
    int len1 = text1.length();
    int len2 = text2.length();
    int[][] dp = new int[len1 + 1][len2 + 1];
    for (int i = 1; i <= len1; i++) {
        for (int j = 1; j <= len2; j++) {
            if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            }
            res = Math.max(dp[i][j], res);
        }
    }
    return res;

}

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

滑动窗口:

  1. 刚开始right向右移动,同时更新max。直到出现了重复字符a,right停止移动,然后left向右移动,滑动窗口减小
  2. 当窗口中没有重复字符a时,left停止移动,更新max。
  3. 重复上述过程,直到字符串结尾。

image-20200310201308128

public int lengthOfLongestSubstring_1(String s) {
 if (s.length() == 0 || s == null) {
     return 0;
 }
 char[] chars = s.toCharArray();
 Map<Character, Integer> windows = new HashMap<>();
 int left = 0;
 int right = 0;
 int res = 0;
 //每次移动right,增大滑动窗口时更新res
 for (int i = 0; i < chars.length; i++) {
     windows.put(chars[i], windows.getOrDefault(chars[i], 0) + 1);
     right++;
     while (windows.get(chars[i]) > 1) {
         char c = chars[left];
         windows.put(c, windows.get(c) - 1);
         left++;
     }
     res = Math.max(res, right - left);

 }
 return res;
}

最长回文子序列

dp[i] [j] 表示从i到j的最长回文子序列的长度

状态转移方程

dp[i] [j] = dp[i+1] [j-1] + 2 , s[i] == s[j]

dp[i] [j] = max(dp[i] [j-1] , dp[i+1] [j]) ,s[i] != s[j]

//    动态规划,状态转移方程
public int longestPalindromeSubseq(String s) {
    if (s == null || s.length() == 0) {
        return 0;
    }

    int[][] dp = new int[s.length()][s.length()];
    //        初始化
    for (int i = 0; i < dp.length; i++) {
        dp[i][i] = 1;
    }

    //        状态转移方程
    for (int i = dp.length - 2; i >= 0; i--) {
        for (int j = i + 1; j < dp[i].length; j++) {
            if (s.charAt(i) == s.charAt(j)) {
                dp[i][j] = dp[i + 1][j - 1] + 2;
            } else {
                dp[i][j] = Math.max(dp[i][j - 1], dp[i + 1][j]);
            }
        }
    }

    return dp[0][s.length() - 1];


}

最长回文子串

从中间向两边扩散来判断回文串,需要考虑字符串是奇数还是偶数,因为两者的中心不同。

// 中心扩散法,时间复杂度O(n^2)
public String longestPalindrome(String s) {
    if (s == null || s.length() == 0) {
        return s;
    }

    String res = "";
    for (int i = 0; i < s.length(); i++) {
        String s1 = expandCenter(s, i, i);
        String s2 = expandCenter(s, i, i + 1);
        res = s1.length() > res.length() ? s1 : res;
        res = s2.length() > res.length() ? s2 : res;

    }

    return res;

}

public String expandCenter(String s, int left, int right) {
    while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
        left--;
        right++;
    }
    return s.substring(left + 1, right);
}

数组

动态规划

最长递增子序列

dp[i]表示以i结尾的数组的最长递增子序列的长度

dp[i]=

  1. max( dp[j]+1 ) , nums[i]>nums[j] , 0<=j<i
  2. 1 , nums[i]<=nums[j]
class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0||nums==null){
            return 0;
        }
        int n=nums.length;
        int[] dp=new int[n];
        Arrays.fill(dp,1);
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    dp[i]=Math.max(dp[j]+1,dp[i]);
                }
            }
        }
        int maxLen=0;
        for(int i=0;i<n;i++){
            maxLen=Math.max(maxLen,dp[i]);
        }
        return maxLen;

    }
}

最长递增子序列的个数

count[i] 表示以i结尾的数组最长递增子序列的个数

count[i] =

当nums[i] > nums[j] , 0<=j<i时

dp[j]+1==dp[i], count[i]=count[j]+count[i]

dp[j]+1>dp[i], count[i] = count[j]

class Solution {
    public int findNumberOfLIS(int[] nums) {
        if(nums.length==0||nums==null){
            return 0;
        }
        int n=nums.length;
        int[] dp=new int[n];
        int[] count=new int[n];
        Arrays.fill(dp,1);
        Arrays.fill(count,1);
        
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]){
                    if(dp[j]+1==dp[i]){
                        count[i]+=count[j];
                    }
                    if(dp[j]+1>dp[i]){
                        count[i]=count[j];
                    }
                    dp[i]=Math.max(dp[j]+1,dp[i]);
                    
                }
            }
        }
        int maxLen=0;
        for(int i=0;i<n;i++){
            maxLen=Math.max(maxLen,dp[i]);
        }
        return maxLen;

    }
}

最长递增子串

dp[i] 表示以i结尾的数组最长递增子串的长度

dp[i]=

  1. dp[i-1] +1 , nums[i]>nums[i-1], 1<=i<n
  2. 1 , nums[i]<=nums[i-1], 1<=i<n
public int findLengthOfLCIS(int[] nums) {
    if (nums.length == 0 || nums == null) {
        return 0;
    }
    if (nums.length == 1) {
        return 1;
    }
    int n = nums.length;
    int[] dp = new int[n];
    Arrays.fill(dp, 1);
    int res = 0;
    for (int i = 1; i < n; i++) {
        if (nums[i] > nums[i - 1]) {
            dp[i] = dp[i - 1] + 1;
        }
        res = Math.max(res, dp[i]);
    }
    return res;

}

猜你喜欢

转载自www.cnblogs.com/dockerchen/p/12458709.html