剑指offer-68.最长不含重复字符的子字符串

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zxm1306192988/article/details/82079433

LeetCode

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

示例 1:

输入: “abcabcbb”
输出: 3
解释: 无重复字符的最长子串是 “abc”,其长度为 3。

示例 2:

输入: “bbbbb”
输出: 1
解释: 无重复字符的最长子串是 “b”,其长度为 1。

示例 3:

输入: “pwwkew”
输出: 3
解释: 无重复字符的最长子串是 “wke”,其长度为 3。
请注意,答案必须是一个子串,”pwke” 是一个子序列 而不是子串。

题解:
方法一:动态规划

dp[i]表示以下标为 i 的字符结尾不包含重复字符的最长子字符串长度
遍历每个位置,向前回溯以上一个字符结尾的最长子字符串长度个位置,看当前字符是否包含在 以上一个字符结尾的最长子字符串中,如果包含 dp[i]=i- 上一个包含的字符的位置。如果不包含,dp[i]=dp[i-1]+1 。
用一个变量记录 遍历过程中最大的 dp 值。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        int[] dp = new int[s.length()]; // dp[i]表示以下标为i的字符结尾不包含重复字符的最长子字符串长度
        dp[0] = 1;
        int maxdp = 1;// 记录最大的值
        for (int dpIndex = 1; dpIndex < dp.length; dpIndex++) { // 遍历每个位置
            int startP = dpIndex - dp[dpIndex - 1]; //以上一个位置结尾的,不包含重复字符的最长子字符串的起始位置
            int i;
            for (i = dpIndex - 1; i >= startP; i--) {// 从前一个位置回溯,看当前字符是否包含在  以上一个位置结尾的不包含重复字符的最长子字符串中
                if (s.charAt(dpIndex) == s.charAt(i)) {//如果包含
                    break;
                }
            }
            dp[dpIndex] = dpIndex - i;//以当前位置结尾的不包含重复字符的最长子字符串长度
            if (dp[dpIndex] > maxdp) {
                maxdp = dp[dpIndex];
            }
        }
        return maxdp;
    }
}

方法二:在方法一基础上进行状态压缩
由于dp[i] 只与 dp[i-1] 有关,所以可以不用申请一个数组 dp[] ,只需要用一个变量 dpPre 保存以上一个字符结尾的不包含重复字符的最长子字符串的长度即可。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        int dpPre = 1; // 以上一个字符结尾的不包含重复字符的最长子字符串的长度
        int maxdp = 1;// 记录最大的值
        for (int dpIndex = 1; dpIndex < s.length(); dpIndex++) { // 遍历每个位置
            int startP = dpIndex - dpPre; //以上一个位置结尾的,不包含重复字符的最长子字符串的起始位置
            int i;
            for (i = dpIndex - 1; i >= startP; i--) {// 从前一个位置回溯,看当前字符是否包含在  以上一个位置结尾的不包含重复字符的最长子字符串中
                if (s.charAt(dpIndex) == s.charAt(i)) {//如果包含
                    break;
                }
            }
            dpPre = dpIndex - i;//以当前位置结尾的不包含重复字符的最长子字符串长度
            if (dpPre > maxdp) {
                maxdp = dpPre;
            }
        }
        return maxdp;
    }
}

方法三:空间换时间,用一个数组 int[] position = new int[128]; 记录上个字符出现的位置。没用每次回溯寻找。
1、若第i个字符在之前没出现过,则 dp(i) = dp(i-1) + 1; 以 i 位置结尾的无重复字符串的最大长度,为以上一个位置结尾的无重复字符串的最大长度+1。

2、若第 i 个字符在之前出现过,计算第i个字符距离上次出现之间的距离为d
(a)若d <= dp(i-1),则说明第i个字符上次出现在dp(i-1)对应的不重复字符串之内,那么这时候更新 dp(i) = d
(b)若d > dp(i-1),则无影响, dp(i) = dp(i-1) + 1。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0) {
            return 0;
        }
        int dpPre = 0; // 以上一个字符结尾的不包含重复字符的最长子字符串的长度
        int maxdp = 0;// 记录最大的值
        int[] position = new int[128]; // 记录上个字符出现的位置
        for (int i = 0; i < 128; i++)
            position[i] = -1;
        for (int dpIndex = 0; dpIndex < s.length(); dpIndex++) { // 遍历每个位置
            int preIndex = position[s.charAt(dpIndex)];
            if (preIndex < 0 || dpIndex - preIndex > dpPre) {
                dpPre++;
            } else {
                dpPre = dpIndex - preIndex;
            }
            if (dpPre > maxdp) {
                maxdp = dpPre;
            }
            position[s.charAt(dpIndex)] = dpIndex;
        }
        return maxdp;
    }
}

猜你喜欢

转载自blog.csdn.net/zxm1306192988/article/details/82079433