Java 最长子串、子序列问题

Java 算法之最长子串、最长公共子序列、最长公共子串、最长回文串

1. 无重复字符的最长子串(对应力扣题3)

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

可以使用「滑动窗口」来解决这个问题:

  1. 我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,其中左指针代表着窗口的左边界「枚举子串的起始位置」,而右指针代表窗口的右边界
    在每一步的操作中,我们会将左指针向右移动一格,表示我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;
    在枚举结束后,我们找到的最长的子串的长度即为答案。
  2. 判断重复字符:
    在上面的流程中,我们还需要使用一种数据结构来判断 是否有重复的字符,常用的数据结构为哈希集合(即 Java 中的 HashSet)。在左指针向右移动的时候,我们从哈希集合中移除一个字符,在右指针向右移动的时候,我们往哈希集合中添加一个字符。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-by-leetc-2/
来源:力扣(LeetCode)

    //最长无重复字符子串
    public static int longestNoRepeatSubstring(String str){
   
        int len = str.length();
        Set<Character> set = new HashSet<>();
        int slidePtr = 0;
        int maxLen = 0;
        for(int i = 0; i < len; i++){
   
            while(slidePtr < len && !set.contains(str.charAt(slidePtr))){
   
                set.add(str.charAt(slidePtr));
                slidePtr++;
            }
            maxLen = Math.max(maxLen, slidePtr-i);
            set.remove(str.charAt(i));
        }
        return maxLen;
    }

2. 最长公共子序列

给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
    两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
  • 示例 1:
输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

解题思路

求两个数组或者字符串的最长公共子序列问题,肯定是要用 动态规划

首先,区分两个概念:子序列可以是不连续的;子数组(子字符串)需要是连续的;
另外,动态规划也是有套路的:单个数组或者字符串要用动态规划时,可以把动态规划 dp[i] 定义为 nums[0:i] 中想要求的结果;当两个数组或者字符串要用动态规划时,可以把动态规划定义成两维的 dp[i][j],其含义是在 A[0:i]B[0:j] 之间匹配得到的想要的结果。

  1. 状态定义
    比如对于本题而言,可以定义dp[i][j]表示 text1[0:i-1] text2[0:j-1] 的最长公共子序列。 (注:text1[0:i-1] 表示的是 text1 的 第 0 个元素到第 i-1 个元素,两端都包含)
    之所以 dp[i][j] 的定义不是 text1[0:i]text2[0:j] ,是为了方便当 i = 0 或者 j = 0 的时候,dp[i][j]表示的为空字符串和另外一个字符串的匹配,这样 dp[i][j] 可以初始化为 0.

  2. 状态转移方程

知道状态定义之后,我们开始写状态转移方程。

text1[i - 1] == text2[j - 1] 时,说明两个子字符串的最后一位相等,所以最长公共子序列又增加了 1,所以 dp[i][j] = dp[i - 1][j - 1] + 1;举个例子,比如对于 ac 和 bc 而言,他们的最长公共子序列的长度等于 a 和 b 的最长公共子序列长度 0 + 1 = 1。
text1[i - 1] != text2[j - 1] 时,说明两个子字符串的最后一位不相等,那么此时的状态 dp[i][j] 应该是 dp[i - 1][j] dp[i][j - 1] 的最大值。举个例子,比如对于 ace 和 bc 而言,他们的最长公共子序列的长度等于 ① ace 和 b 的最长公共子序列长度0 与 ② ac 和 bc 的最长公共子序列长度1 的最大值,=1。
综上状态转移方程为:
d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 , 当 t e x t 1 [ i − 1 ] = = t e x t 2 [ j − 1 ] ; dp[i][j] = dp[i - 1][j - 1] + 1, 当 text1[i - 1] == text2[j - 1]; dp[i][j]=dp[i1][j1]+1,text1[i

你可能感兴趣的:(java,开发语言,后端)